resolved merge conflicts

This commit is contained in:
John Yeo
2026-05-05 10:43:22 +08:00
314 changed files with 20673 additions and 4225 deletions

5
.gitignore vendored
View File

@@ -16,6 +16,7 @@ supabase.sh
**/dist/
**/.next/
**/.vercel/
**/.turbo/
**/.DS_Store
**/.env*
tests/
@@ -141,4 +142,6 @@ TAKEHOME.md
.agents/
.mcp.json
.opencode/skills/
AGENTS.md
AGENTS.md
server/.turbo

View File

@@ -1,14 +1,13 @@
set -e
bun knip
(cd server && bun ts)
changed_files="$(git diff --cached --name-only)"
bun knip
if printf '%s\n' "$changed_files" | grep -q '^server/'; then
(cd server && bun test tests/unit)
bunx turbo run ts --filter=@autumn/server
fi
if printf '%s\n' "$changed_files" | grep -q '^vite/'; then
(cd vite && bun test tests/)
bunx turbo run ts --filter=@autumn/vite
fi

22
.husky/pre-push Normal file
View File

@@ -0,0 +1,22 @@
set -e
zero_sha="0000000000000000000000000000000000000000"
changed_files="$(
while read local_ref local_sha remote_ref remote_sha; do
if [ "$local_sha" = "$zero_sha" ]; then
continue
fi
if [ "$remote_sha" = "$zero_sha" ]; then
base="$(git merge-base "$local_sha" origin/dev)"
else
base="$remote_sha"
fi
git diff --name-only "$base" "$local_sha"
done | sort -u
)"
# if printf '%s\n' "$changed_files" | grep -q '^vite/'; then
# bunx turbo run test:unit --filter=@autumn/vite
# fi

View File

@@ -34,6 +34,22 @@
"type": "remote",
"url": "https://mcp.incident.io/mcp",
"oauth": {}
},
"plain": {
"type": "remote",
"url": "https://mcp.plain.com/mcp",
"oauth": {}
},
"autumn-internal": {
"type": "local",
"command": [
"sh",
"-c",
"cd \"/Users/johnyeocx/Autumn/autumn-cloud\" && exec infisical run --env=prod --recursive -- bun run \"/Users/johnyeocx/Autumn/autumn-cloud/ai/src/mcp/index.ts\""
],
"env": {
"AUTUMN_REPO_ROOT": "/Users/johnyeocx/Autumn"
}
}
},
"plugin": [

View File

@@ -1,5 +1,42 @@
<!-- Generated by ai-sync. Edit ai/rules/ instead. -->
# Autumn Shared Utils
Before writing inline `.filter()`, `.find()`, `.some()`, boolean predicate, or `<src>To<dst>` transform logic over Autumn objects (`Price`, `Entitlement`, `FullCusProduct`, `FullCustomer`, `Feature`, etc.), **check `autumn/shared/utils/` for an existing helper.** Reaching for `array.filter(... === id)` directly is almost always a sign the utility was missed.
The package is organized resource-first, pattern-second. Within each `<resource>Utils/` folder:
- `classify*/``is*` boolean predicates (`isPrepaidPrice`, `isCustomerProductPaidRecurring`)
- `convert*/``<src>To<dst>` transforms (`cusProductToPrices`, `entToPrice`)
- `find*/``Array.find` lookups (`findFeatureById`, `findPriceByFeatureId`)
- `filter*/``Array.filter` collections (`filterCustomerProductsByFeatureId`)
- `enrich*` files — augment with joined data (`enrichEntitlementWithFeature`)
**If the helper you need doesn't exist, ALWAYS ask the user before adding one.** Naming and folder placement are cross-cutting and non-trivial — wrong placement clutters `@autumn/shared` for every consumer.
Full convention (folder tree, naming nuances, anti-patterns): see the `shared-utils` skill.
# Installing External Skills
Third-party skills installed via `bunx skills add <pkg>` land under each agent's local skill dir (`.claude/skills/`, `.cursor/skills/`, etc.). Those locations are NOT a source of truth — `bun ai sync` only reads from `ai/config/skills/**` and prunes anything else it manages, so a raw `bunx skills add` will not propagate to the other consumer repos (autumn, cloud).
## Workflow
1. Install via the CLI as usual:
```sh
bunx skills add <owner>/<repo>
```
2. Move the installed skill folder(s) into `ai/config/skills/external/<skill-name>/`. `external/` is core, so both `autumn` and `cloud` consume it. Use `cloud/external/` only if the skill references cloud-only code.
3. Delete the leftover copies from `.claude/skills/`, `.cursor/skills/`, `.agents/skills/`, `.opencode/skills/` — `bun ai sync` will recreate them as symlinks.
4. Run `bun ai sync` to symlink the skill into every agent dir across every repo that pulls the `ai/` submodule.
## Notes
- Skill folder names must be globally unique across `ai/config/skills/**` (sync flattens them).
- Keep upstream `SKILL.md` frontmatter intact — `name` and `description` drive when the agent loads it. Only edit if the description is not specific enough about WHEN to use the skill.
- If the skill ships a `references/` or `scripts/` subfolder, copy the whole directory tree, not just `SKILL.md`.
- Re-running `bunx skills add` upstream-updates: install fresh, diff against `ai/config/skills/external/<name>/`, then promote the changes.
# Scope Cache Refresh Changes Safely
When changing cache-refresh behavior for API routes:

2
ai

Submodule ai updated: 18d68d3c2a...e21275f18e

View File

@@ -8,7 +8,7 @@ import { DynamicResponseField } from "/components/dynamic-response-field.jsx";
import { DynamicResponseExample } from "/components/dynamic-response-example.jsx";
<Note>
The update endpoint modifies an existing subscription. Use this to change prepaid quantities, cancel subscriptions, or modify plan configuration. For subscribing to a new plan, use [attach](/api-reference/billing/billingAttach) instead.
The update endpoint modifies an existing subscription. Use this to change prepaid quantities, cancel subscriptions, or modify plan configuration. For subscribing to a new plan, use [attach](/api-reference/billing/attach) instead.
</Note>
### Common Use Cases

View File

@@ -7,7 +7,7 @@ import { DynamicParamField } from "/components/dynamic-param-field.jsx";
import { DynamicResponseField } from "/components/dynamic-response-field.jsx";
import { DynamicResponseExample } from "/components/dynamic-response-example.jsx";
Creates a new plan with optional base price and feature configurations. See [How plans work](/documentation/pricing/plans) for concepts and [Adding features to plans](/documentation/pricing/plan-features) for item configuration.
Creates a new plan with optional base price and feature configurations. See [How plans work](/documentation/concepts/plans) for concepts and [Adding features to plans](/documentation/concepts/plan-items) for item configuration.
### Plan Configuration

View File

@@ -7,7 +7,7 @@ import { DynamicParamField } from "/components/dynamic-param-field.jsx";
import { DynamicResponseField } from "/components/dynamic-response-field.jsx";
import { DynamicResponseExample } from "/components/dynamic-response-example.jsx";
Updates an existing plan. By default, creates a new version of the plan. See [Adding features to plans](/documentation/pricing/plan-features) for item configuration.
Updates an existing plan. By default, creates a new version of the plan. See [Adding features to plans](/documentation/concepts/plan-items) for item configuration.
<Note>
Updates create a new plan version by default. Existing customers remain on their current version until their subscription renews or they explicitly upgrade.

View File

@@ -382,6 +382,10 @@ This is useful for attaching custom metadata to the Stripe subscription created
If true, skips any billing changes for the attach operation.
</DynamicParamField>
<DynamicParamField body="enable_plan_immediately" type="boolean">
If true, the customer's plan is activated immediately even when payment is deferred (invoice mode) or pending (Stripe checkout). For Stripe checkout, the customer_product is inserted before the customer completes the hosted form.
</DynamicParamField>
### Response

View File

@@ -255,6 +255,10 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
Only applicable when the customer has an existing Stripe subscription. If true, creates a new separate subscription instead of merging into the existing one.
</DynamicParamField>
<DynamicParamField body="enable_plan_immediately" type="boolean">
If true, the cusProducts are activated immediately even when payment is pending via Stripe checkout.
</DynamicParamField>
<DynamicParamField body="customer_data" type="object">
Customer details to set when creating a customer
<Expandable title="properties">

View File

@@ -315,6 +315,10 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
If true, skips any billing changes for the attach operation.
</DynamicParamField>
<DynamicParamField body="enable_plan_immediately" type="boolean">
If true, the customer's plan is activated immediately even when payment is deferred (invoice mode) or pending (Stripe checkout). For Stripe checkout, the customer_product is inserted before the customer completes the hosted form.
</DynamicParamField>
### Response
@@ -769,6 +773,16 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">
@@ -1084,6 +1098,16 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">
@@ -1149,6 +1173,32 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
The type of checkout that will be used if the customer is redirected to a checkout page.
</DynamicResponseField>
<DynamicResponseField name="tax" type="object">
Tax preview for the immediate charge. Contact us to enable the tax flag on your organisation. Shows only with flag enabled, a Stripe customer exists and has a location.
<Expandable title="properties">
<DynamicResponseField name="total" type="number">
Total tax amount in major currency units.
</DynamicResponseField>
<DynamicResponseField name="amount_inclusive" type="number">
Tax included in line item subtotals.
</DynamicResponseField>
<DynamicResponseField name="amount_exclusive" type="number">
Tax added on top of subtotals.
</DynamicResponseField>
<DynamicResponseField name="currency" type="string">
Three-letter currency code.
</DynamicResponseField>
<DynamicResponseField name="status" type="'complete' | 'incomplete'">
Calculation status ('complete' when Stripe Tax succeeds or 'incomplete' when Stripe Tax returned 0 or errored).
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<ResponseExample>
```json 200

View File

@@ -255,6 +255,10 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
Only applicable when the customer has an existing Stripe subscription. If true, creates a new separate subscription instead of merging into the existing one.
</DynamicParamField>
<DynamicParamField body="enable_plan_immediately" type="boolean">
If true, the cusProducts are activated immediately even when payment is pending via Stripe checkout.
</DynamicParamField>
<DynamicParamField body="customer_data" type="object">
Customer details to set when creating a customer
<Expandable title="properties">
@@ -942,6 +946,16 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">
@@ -1257,6 +1271,16 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">
@@ -1322,6 +1346,32 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
The type of checkout that will be used if the customer is redirected to a checkout page.
</DynamicResponseField>
<DynamicResponseField name="tax" type="object">
Tax preview for the immediate charge. Contact us to enable the tax flag on your organisation. Shows only with flag enabled, a Stripe customer exists and has a location.
<Expandable title="properties">
<DynamicResponseField name="total" type="number">
Total tax amount in major currency units.
</DynamicResponseField>
<DynamicResponseField name="amount_inclusive" type="number">
Tax included in line item subtotals.
</DynamicResponseField>
<DynamicResponseField name="amount_exclusive" type="number">
Tax added on top of subtotals.
</DynamicResponseField>
<DynamicResponseField name="currency" type="string">
Three-letter currency code.
</DynamicResponseField>
<DynamicResponseField name="status" type="'complete' | 'incomplete'">
Calculation status ('complete' when Stripe Tax succeeds or 'incomplete' when Stripe Tax returned 0 or errored).
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<ResponseExample>
```json 200

View File

@@ -718,6 +718,16 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">
@@ -1033,6 +1043,16 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">

View File

@@ -285,6 +285,10 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
If true, skips any billing changes for the attach operation.
</DynamicParamField>
<DynamicParamField body="enable_plan_immediately" type="boolean">
If true, the customer's plan is activated immediately even when payment is deferred (invoice mode) or pending (Stripe checkout). For Stripe checkout, the customer_product is inserted before the customer completes the hosted form.
</DynamicParamField>
### Response

View File

@@ -233,7 +233,7 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
</DynamicResponseField>
<DynamicResponseField name="purchase_limit" type="object">
Optional rate limit to cap how often auto top-ups occur.
Optional rate limit to cap how often auto top-ups occur. Expand billing_controls.auto_topups.purchase_limit for a count of top ups and the next_reset_at.
<Expandable title="properties">
<DynamicResponseField name="interval" type="'hour' | 'day' | 'week' | 'month'">
The time interval for the purchase limit window.
@@ -576,6 +576,16 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">
@@ -909,6 +919,16 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">

View File

@@ -104,7 +104,7 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
</DynamicResponseField>
<DynamicResponseField name="purchase_limit" type="object">
Optional rate limit to cap how often auto top-ups occur.
Optional rate limit to cap how often auto top-ups occur. Expand billing_controls.auto_topups.purchase_limit for a count of top ups and the next_reset_at.
<Expandable title="properties">
<DynamicResponseField name="interval" type="'hour' | 'day' | 'week' | 'month'">
The time interval for the purchase limit window.
@@ -447,6 +447,16 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">
@@ -780,6 +790,16 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">

View File

@@ -221,7 +221,7 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
</DynamicResponseField>
<DynamicResponseField name="purchase_limit" type="object">
Optional rate limit to cap how often auto top-ups occur.
Optional rate limit to cap how often auto top-ups occur. Expand billing_controls.auto_topups.purchase_limit for a count of top ups and the next_reset_at.
<Expandable title="properties">
<DynamicResponseField name="interval" type="'hour' | 'day' | 'week' | 'month'">
The time interval for the purchase limit window.
@@ -564,6 +564,16 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">
@@ -897,6 +907,16 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">

View File

@@ -529,6 +529,16 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">
@@ -861,6 +871,16 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">

View File

@@ -301,6 +301,16 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">
@@ -633,6 +643,16 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">

View File

@@ -365,6 +365,16 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">
@@ -697,6 +707,16 @@ import { DynamicResponseExample } from "/components/dynamic-response-example.jsx
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">

View File

@@ -312,6 +312,16 @@ await autumn.plans.create({
</Expandable>
</DynamicParamField>
<DynamicParamField body="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicParamField body="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicParamField>
</Expandable>
</DynamicParamField>
### Response
@@ -563,6 +573,16 @@ await autumn.plans.create({
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">
@@ -647,7 +667,10 @@ await autumn.plans.create({
"createdAt": 1771513979217,
"env": "sandbox",
"archived": false,
"baseVariantId": null
"baseVariantId": null,
"config": {
"ignore_past_due": false
}
}
```
</ResponseExample>

View File

@@ -289,6 +289,16 @@ const plan = await autumn.plans.get({
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">
@@ -373,7 +383,10 @@ const plan = await autumn.plans.get({
"createdAt": 1771513979217,
"env": "sandbox",
"archived": false,
"baseVariantId": null
"baseVariantId": null,
"config": {
"ignore_past_due": false
}
}
```
</ResponseExample>

View File

@@ -306,6 +306,16 @@ const plans = await autumn.plans.list({
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">
@@ -395,7 +405,10 @@ const plans = await autumn.plans.list({
"createdAt": 1771513979217,
"env": "sandbox",
"archived": false,
"baseVariantId": null
"baseVariantId": null,
"config": {
"ignore_past_due": false
}
}
]
}

View File

@@ -239,6 +239,16 @@ await autumn.plans.update({
</Expandable>
</DynamicParamField>
<DynamicParamField body="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicParamField body="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicParamField>
</Expandable>
</DynamicParamField>
<DynamicParamField body="version" type="number" />
<DynamicParamField body="archived" type="boolean" />
@@ -498,6 +508,16 @@ await autumn.plans.update({
If this is a variant, the ID of the base plan it was created from.
</DynamicResponseField>
<DynamicResponseField name="config" type="object">
Miscellaneous plan-level configuration flags.
<Expandable title="properties">
<DynamicResponseField name="ignore_past_due" type="boolean">
If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state.
</DynamicResponseField>
</Expandable>
</DynamicResponseField>
<DynamicResponseField name="customer_eligibility" type="object">
<Expandable title="properties">
<DynamicResponseField name="trial_available" type="boolean">
@@ -582,7 +602,10 @@ await autumn.plans.update({
"createdAt": 1771513979217,
"env": "sandbox",
"archived": false,
"baseVariantId": null
"baseVariantId": null,
"config": {
"ignore_past_due": false
}
}
```
</ResponseExample>

View File

@@ -0,0 +1,56 @@
---
title: "Auto Top-Up Succeeded"
openapi: "api/openapi.yml webhook billing.auto_topup_succeeded"
---
### Payload Fields
<ParamField body="customer_id" type="string" required>
The ID of the customer whose balance was topped up.
</ParamField>
<ParamField body="feature_id" type="string" required>
The feature ID that was automatically topped up.
</ParamField>
<ParamField body="quantity_granted" type="number" required>
The normalized amount of balance granted by the top-up.
</ParamField>
<ParamField body="threshold" type="number" required>
The configured balance threshold that triggered the top-up.
</ParamField>
<ParamField body="balance_after" type="number" required>
The customer's remaining balance for the feature after the top-up.
</ParamField>
<ParamField body="invoice_mode" type="boolean" required>
Whether the auto top-up created a `send_invoice` invoice instead of auto-charging the saved payment method.
</ParamField>
<ParamField body="invoice" type="object" required>
The invoice created for the auto top-up.
<Expandable title="properties">
<ParamField body="stripe_id" type="string" required>
The Stripe invoice ID. Use this as a stable dedupe key.
</ParamField>
<ParamField body="status" type="string">
The status of the invoice. `"paid"` for auto-charged top-ups; `"open"` for `invoice_mode` top-ups where credits were granted but the invoice has not yet been paid.
</ParamField>
<ParamField body="total" type="number" required>
The total amount of the invoice in the smallest currency unit (e.g. cents for USD), matching Stripe's `invoice.total`.
</ParamField>
<ParamField body="currency" type="string" required>
The ISO currency code for the invoice.
</ParamField>
<ParamField body="hosted_invoice_url" type="string">
URL to the hosted invoice page, if available.
</ParamField>
</Expandable>
</ParamField>

File diff suppressed because it is too large Load Diff

View File

@@ -130,6 +130,7 @@
"group": "Additional Resources",
"pages": [
"documentation/webhooks",
"documentation/slack-discord-notifications",
"documentation/fail-open",
"documentation/rate-limits",
"documentation/external-providers/convex",
@@ -271,6 +272,12 @@
"api-reference/webhooks/balancesLimitReached"
]
},
{
"group": "Billing",
"pages": [
"api-reference/webhooks/billingAutoTopupSucceeded"
]
},
{
"group": "Vercel",
"pages": [

View File

@@ -170,4 +170,8 @@ This limits the customer to 5 auto top-ups per month. Supported intervals: `hour
<Note>
Auto top-ups use burst suppression to prevent duplicate purchases when multiple track events happen in quick succession. There's a 30-second cooldown between top-ups for the same feature.
</Note>
</Note>
## Notifications
Subscribe to the [`billing.auto_topup_succeeded`](/api-reference/webhooks/billingAutoTopupSucceeded) webhook to be notified when a top-up grants credits. The payload includes the granted quantity, the new balance, and the underlying invoice — useful for sending receipts, updating internal ledgers, or reconciling balance after a recharge.

View File

@@ -0,0 +1,84 @@
---
title: "Slack and Discord Notifications"
description: "Send Autumn webhook events to Slack or Discord channels with rich, formatted messages using Svix transformations."
---
import SlackTransform from "/snippets/svix-transforms/slack.mdx";
import DiscordTransform from "/snippets/svix-transforms/discord.mdx";
Forward Autumn webhook events to a Slack or Discord channel with ready-made
[Svix transformations](https://docs.svix.com/transformations) that turn each
event into a clean, formatted message.
## Supported events
| Event | Slack | Discord |
| --- | :---: | :---: |
| `customer.products.updated` | ✓ | ✓ |
| `balances.limit_reached` | ✓ | ✓ |
| `balances.usage_alert_triggered` | ✓ | ✓ |
Other event types — including Vercel Marketplace events — are skipped by the
transforms so they are never delivered to your Slack or Discord channel.
## Setup
<Steps>
<Step title="Get an incoming webhook URL">
Follow the official guide for the platform you want to use:
- [Slack — Sending messages using incoming webhooks](https://docs.slack.dev/messaging/sending-messages-using-incoming-webhooks/)
- [Discord — Intro to webhooks](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks)
Both flows give you a webhook URL that looks like
`https://hooks.slack.com/services/...` or
`https://discord.com/api/webhooks/...`. Keep it handy for the next step.
</Step>
<Step title="Add the webhook endpoint in Autumn">
In your Autumn dashboard, go to **Developer → Webhooks** and click
**Add Endpoint**. Paste in the URL from the previous step and select the
events you want to subscribe to.
<Frame>
<img src="/images/svix-notifications/01-add-endpoint.png" alt="Adding a webhook endpoint in the Autumn dashboard" />
</Frame>
</Step>
<Step title="Enable transformations">
Open the endpoint you just created, switch to the **Advanced** tab, and
click **Edit transformation**.
<Frame>
<img src="/images/svix-notifications/02-advanced-tab.png" alt="The Advanced tab on a webhook endpoint, showing the Edit transformation button" />
</Frame>
Paste in the transform code below for the platform you're targeting,
then click **Save and Enable**.
<Frame>
<img src="/images/svix-notifications/03-transformation-editor.png" alt="The transformation editor with the Enabled toggle, code area, and Save and Enable button highlighted" />
</Frame>
</Step>
<Step title="Enable throttling">
Back on the **Advanced** tab, click **Edit** next to **Endpoint Throttling**
and set a sensible RPS (requests-per-second) limit. This prevents your
Slack or Discord channel from being flooded during high-volume events such
as bulk customer migrations or backfills.
<Frame>
<img src="/images/svix-notifications/04-throttling.png" alt="Endpoint throttling editor with an RPS input field" />
</Frame>
</Step>
</Steps>
## Transform code
Copy the transform for your platform into the **Code** editor on the
transformation page, then click **Save and Enable**.
<CodeGroup>
<SlackTransform />
<DiscordTransform />
</CodeGroup>

View File

@@ -97,6 +97,64 @@ For entity-scoped usage, the payload will also include an `entity_id`:
}
```
### billing.auto_topup_succeeded
Fired when an [auto top-up](/documentation/modelling-pricing/auto-top-ups) successfully grants additional prepaid balance. Useful for sending receipts, updating internal ledgers, or reconciling balance after a recharge.
For auto-charged top-ups, the event fires only after the Stripe invoice is `paid`. For `invoice_mode` top-ups, the event fires once credits are granted and the invoice is finalized — `invoice.status` will typically be `"open"` until the customer pays.
Use `invoice.stripe_id` as a stable dedupe key. The top-level `id` field (e.g. `evt_auto_topup_...`) is a unique identifier for the event itself.
**Example payload (auto-charge):**
```json expandable
{
"type": "billing.auto_topup_succeeded",
"id": "evt_auto_topup_2abc123",
"occurred_at": 1761840000000,
"data": {
"customer_id": "user_123",
"feature_id": "credits",
"quantity_granted": 1000,
"threshold": 500,
"balance_after": 1450,
"invoice_mode": false,
"invoice": {
"stripe_id": "in_1A2B3C4D5E6F",
"status": "paid",
"total": 1000,
"currency": "usd",
"hosted_invoice_url": "https://invoice.stripe.com/i/..."
}
}
}
```
**Example payload (invoice mode):**
```json expandable
{
"type": "billing.auto_topup_succeeded",
"id": "evt_auto_topup_3xyz456",
"occurred_at": 1761840000000,
"data": {
"customer_id": "user_123",
"feature_id": "credits",
"quantity_granted": 1000,
"threshold": 500,
"balance_after": 1450,
"invoice_mode": true,
"invoice": {
"stripe_id": "in_2G3H4I5J6K7L",
"status": "open",
"total": 1000,
"currency": "usd",
"hosted_invoice_url": "https://invoice.stripe.com/i/..."
}
}
}
```
### balances.usage_alert_triggered
Fired when a customer crosses a configured usage alert threshold. Usage alerts let you monitor when customers approach or exceed specific usage levels for a feature.

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,248 @@
{/* AUTO-GENERATED by syncSvixTransforms in packages/openapi/utils/mintlifyTransform/index.ts */}
{/* Source: others/svix-transforms/discord.ts */}
{/* Edit the source file, not this snippet. */}
```js Discord
/**
* @param webhook the webhook object
* @param webhook.method destination method. Allowed values: "POST", "PUT"
* @param webhook.url current destination address
* @param webhook.eventType current webhook Event Type
* @param webhook.payload JSON payload
* @param webhook.cancel whether to cancel dispatch of the given webhook
*/
function handler(webhook) {
var AUTUMN_BASE = "https://app.useautumn.com/customers/";
var AUTUMN_USERNAME = "Autumn";
var AUTUMN_AVATAR_URL = "https://i.ibb.co/BHCF1ZqL/autumnicon.png";
// Discord embed colors (decimal RGB).
var COLOR_SUCCESS = 0x22c55e;
var COLOR_INFO = 0x3b82f6;
var COLOR_WARNING = 0xf59e0b;
var COLOR_DANGER = 0xef4444;
var COLOR_NEUTRAL = 0x6b7280;
var payload = webhook.payload || {};
var data = payload.data || payload;
// ============ customer.products.updated ============
if (webhook.eventType === "customer.products.updated") {
var scenario = data.scenario || "updated";
var customer = data.customer || {};
var entity = data.entity || null;
var product = data.updated_product || {};
var customerName = customer.name || customer.email || customer.id || "Customer";
var customerEmail = customer.email || null;
var customerId = customer.id || "";
var productName = product.name || "their plan";
if (product.version && product.version !== 1) {
productName = productName + " V" + product.version;
}
var entityLabel = null;
if (entity) {
entityLabel = entity.name || entity.id || null;
}
var emoji = "🔔";
var header = "Subscription Updated";
var verb = "updated";
var color = COLOR_NEUTRAL;
if (scenario === "new") {
emoji = "🎉"; header = "New Subscription"; verb = "subscribed to"; color = COLOR_SUCCESS;
} else if (scenario === "upgrade") {
emoji = "🚀"; header = "Customer Upgraded"; verb = "upgraded to"; color = COLOR_SUCCESS;
} else if (scenario === "downgrade") {
emoji = "📉"; header = "Customer Downgraded"; verb = "downgraded to"; color = COLOR_WARNING;
} else if (scenario === "cancel") {
emoji = "⚠️"; header = "Subscription Cancelled"; verb = "cancelled"; color = COLOR_WARNING;
} else if (scenario === "renew") {
emoji = "🔄"; header = "Subscription Uncancelled"; verb = "uncancelled"; color = COLOR_SUCCESS;
} else if (scenario === "expired") {
emoji = "💀"; header = "Subscription Expired"; verb = "expired on"; color = COLOR_DANGER;
} else if (scenario === "scheduled") {
emoji = "📅"; header = "Change Scheduled"; verb = "scheduled a change to"; color = COLOR_INFO;
}
var sentence;
if (scenario === "expired") {
sentence = "**" + customerName + "**'s **" + productName + "** expired";
} else {
sentence = "**" + customerName + "** " + verb + " **" + productName + "**";
}
var description = sentence;
if (customerId) {
description += "\n\n[View in Autumn](" + AUTUMN_BASE + customerId + ")";
}
var fields = [
{ name: "Customer", value: customerName, inline: true }
];
if (customerEmail) {
fields.push({ name: "Email", value: customerEmail, inline: true });
}
fields.push({ name: "Product", value: productName, inline: true });
fields.push({ name: "Scenario", value: "`" + scenario + "`", inline: true });
if (entityLabel) {
fields.push({ name: "Entity", value: entityLabel, inline: true });
}
var embed = {
title: emoji + " " + header,
description: description,
color: color,
fields: fields
};
if (customerId) {
embed.url = AUTUMN_BASE + customerId;
}
var footerParts = [];
if (customerId) footerParts.push("Customer ID: " + customerId);
if (entity && entity.id) footerParts.push("Entity: " + entity.id);
if (footerParts.length > 0) {
embed.footer = { text: footerParts.join(" | ") };
}
webhook.payload = {
username: AUTUMN_USERNAME,
avatar_url: AUTUMN_AVATAR_URL,
embeds: [embed]
};
return webhook;
}
// ============ balances.limit_reached ============
if (webhook.eventType === "balances.limit_reached") {
var lrCustomerId = data.customer_id || "";
var lrFeatureId = data.feature_id || "feature";
var lrLimitType = data.limit_type || "included";
var lrEntityId = data.entity_id || null;
var lrCustomerDisplay = lrCustomerId
? "[`" + lrCustomerId + "`](" + AUTUMN_BASE + lrCustomerId + ")"
: "`unknown`";
var lrDescription =
lrCustomerDisplay + " hit their **" + lrFeatureId + "** `" + lrLimitType + "` limit";
if (lrCustomerId) {
lrDescription += "\n\n[View in Autumn](" + AUTUMN_BASE + lrCustomerId + ")";
}
var lrFields = [
{
name: "Customer",
value: lrCustomerId ? "`" + lrCustomerId + "`" : "—",
inline: true
},
{ name: "Feature", value: "`" + lrFeatureId + "`", inline: true },
{ name: "Limit Type", value: "`" + lrLimitType + "`", inline: true }
];
if (lrEntityId) {
lrFields.push({ name: "Entity", value: "`" + lrEntityId + "`", inline: true });
}
var lrEmbed = {
title: "🚫 Limit Reached",
description: lrDescription,
color: COLOR_DANGER,
fields: lrFields
};
if (lrCustomerId) {
lrEmbed.url = AUTUMN_BASE + lrCustomerId;
}
var lrFooterParts = [];
if (lrCustomerId) lrFooterParts.push("Customer ID: " + lrCustomerId);
if (lrEntityId) lrFooterParts.push("Entity: " + lrEntityId);
if (lrFooterParts.length > 0) {
lrEmbed.footer = { text: lrFooterParts.join(" | ") };
}
webhook.payload = {
username: AUTUMN_USERNAME,
avatar_url: AUTUMN_AVATAR_URL,
embeds: [lrEmbed]
};
return webhook;
}
// ============ balances.usage_alert_triggered ============
if (webhook.eventType === "balances.usage_alert_triggered") {
var uaCustomerId = data.customer_id || "";
var uaFeatureId = data.feature_id || "feature";
var uaEntityId = data.entity_id || null;
var uaAlert = data.usage_alert || {};
var uaAlertName = uaAlert.name || "Usage alert";
var uaThreshold = uaAlert.threshold;
var uaThresholdType = uaAlert.threshold_type || "usage";
var uaThresholdLabel = "—";
if (uaThreshold !== undefined && uaThreshold !== null) {
if (uaThresholdType === "usage_percentage") {
uaThresholdLabel = uaThreshold + "% used";
} else if (uaThresholdType === "remaining_percentage") {
uaThresholdLabel = uaThreshold + "% remaining";
} else if (uaThresholdType === "remaining") {
uaThresholdLabel = uaThreshold + " remaining";
} else {
uaThresholdLabel = uaThreshold + " used";
}
}
var uaCustomerDisplay = uaCustomerId
? "[`" + uaCustomerId + "`](" + AUTUMN_BASE + uaCustomerId + ")"
: "`unknown`";
var uaDescription =
uaCustomerDisplay + " crossed the **" + uaAlertName + "** threshold on **" + uaFeatureId + "**";
if (uaCustomerId) {
uaDescription += "\n\n[View in Autumn](" + AUTUMN_BASE + uaCustomerId + ")";
}
var uaFields = [
{
name: "Customer",
value: uaCustomerId ? "`" + uaCustomerId + "`" : "—",
inline: true
},
{ name: "Feature", value: "`" + uaFeatureId + "`", inline: true },
{ name: "Alert", value: uaAlertName, inline: true },
{ name: "Threshold", value: uaThresholdLabel, inline: true }
];
if (uaEntityId) {
uaFields.push({ name: "Entity", value: "`" + uaEntityId + "`", inline: true });
}
var uaEmbed = {
title: "📊 Usage Alert: " + uaAlertName,
description: uaDescription,
color: COLOR_WARNING,
fields: uaFields
};
if (uaCustomerId) {
uaEmbed.url = AUTUMN_BASE + uaCustomerId;
}
var uaFooterParts = [];
if (uaCustomerId) uaFooterParts.push("Customer ID: " + uaCustomerId);
if (uaEntityId) uaFooterParts.push("Entity: " + uaEntityId);
if (uaFooterParts.length > 0) {
uaEmbed.footer = { text: uaFooterParts.join(" | ") };
}
webhook.payload = {
username: AUTUMN_USERNAME,
avatar_url: AUTUMN_AVATAR_URL,
embeds: [uaEmbed]
};
return webhook;
}
// Unmatched event type — cancel dispatch.
webhook.cancel = true;
return webhook;
}
```

View File

@@ -0,0 +1,298 @@
{/* AUTO-GENERATED by syncSvixTransforms in packages/openapi/utils/mintlifyTransform/index.ts */}
{/* Source: others/svix-transforms/slack.ts */}
{/* Edit the source file, not this snippet. */}
```js Slack
/**
* @param webhook the webhook object
* @param webhook.method destination method. Allowed values: "POST", "PUT"
* @param webhook.url current destination address
* @param webhook.eventType current webhook Event Type
* @param webhook.payload JSON payload
* @param webhook.cancel whether to cancel dispatch of the given webhook
*/
function handler(webhook) {
var AUTUMN_BASE = "https://app.useautumn.com/customers/";
var AUTUMN_USERNAME = "Autumn";
var AUTUMN_ICON_URL = "https://i.ibb.co/BHCF1ZqL/autumnicon.png";
var payload = webhook.payload || {};
var data = payload.data || payload;
// ============ customer.products.updated ============
if (webhook.eventType === "customer.products.updated") {
var scenario = data.scenario || "updated";
var customer = data.customer || {};
var entity = data.entity || null;
var product = data.updated_product || {};
var customerName = customer.name || customer.email || customer.id || "Customer";
var customerEmail = customer.email || null;
var customerId = customer.id || "";
var productName = product.name || "their plan";
if (product.version && product.version !== 1) {
productName = productName + " V" + product.version;
}
var entityLabel = null;
if (entity) {
entityLabel = entity.name || entity.id || null;
}
var meta = {
"new": { emoji: "🎉", header: "New Subscription", verb: "subscribed to" },
"upgrade": { emoji: "🚀", header: "Customer Upgraded", verb: "upgraded to" },
"downgrade": { emoji: "📉", header: "Customer Downgraded", verb: "downgraded to" },
"cancel": { emoji: "⚠️", header: "Subscription Cancelled", verb: "cancelled" },
"renew": { emoji: "🔄", header: "Subscription Uncancelled", verb: "uncancelled" },
"expired": { emoji: "💀", header: "Subscription Expired", verb: "expired on" },
"scheduled": { emoji: "📅", header: "Change Scheduled", verb: "scheduled a change to" }
}[scenario] || { emoji: "🔔", header: "Subscription Updated", verb: "updated" };
var sentence;
if (scenario === "expired") {
sentence = "*" + customerName + "*'s *" + productName + "* expired";
} else {
sentence = "*" + customerName + "* " + meta.verb + " *" + productName + "*";
}
var previewText = meta.emoji + " " + customerName + " " + meta.verb + " " + productName;
var fields = [
{ type: "mrkdwn", text: "*Customer:*\n" + customerName }
];
if (customerEmail) {
fields.push({ type: "mrkdwn", text: "*Email:*\n" + customerEmail });
}
fields.push({ type: "mrkdwn", text: "*Product:*\n" + productName });
fields.push({ type: "mrkdwn", text: "*Scenario:*\n`" + scenario + "`" });
if (entityLabel) {
fields.push({ type: "mrkdwn", text: "*Entity:*\n" + entityLabel });
}
var contextParts = [];
if (customerId) {
contextParts.push("Customer ID: `" + customerId + "`");
}
if (entity && entity.id) {
contextParts.push("Entity: `" + entity.id + "`");
}
var blocks = [
{
type: "header",
text: { type: "plain_text", text: meta.emoji + " " + meta.header, emoji: true }
},
{
type: "section",
text: { type: "mrkdwn", text: sentence }
},
{
type: "section",
fields: fields
}
];
if (customerId) {
blocks.push({
type: "actions",
elements: [
{
type: "button",
text: { type: "plain_text", text: "View in Autumn", emoji: true },
url: AUTUMN_BASE + customerId,
style: "primary"
}
]
});
}
if (contextParts.length > 0) {
blocks.push({
type: "context",
elements: [
{ type: "mrkdwn", text: contextParts.join(" | ") }
]
});
}
webhook.payload = {
username: AUTUMN_USERNAME,
icon_url: AUTUMN_ICON_URL,
text: previewText,
blocks: blocks
};
return webhook;
}
// ============ balances.limit_reached ============
if (webhook.eventType === "balances.limit_reached") {
var lrCustomerId = data.customer_id || "";
var lrFeatureId = data.feature_id || "feature";
var lrLimitType = data.limit_type || "included";
var lrEntityId = data.entity_id || null;
var lrCustomerLink = lrCustomerId
? "<" + AUTUMN_BASE + lrCustomerId + "|`" + lrCustomerId + "`>"
: "`unknown`";
var lrSentence =
lrCustomerLink + " hit their *" + lrFeatureId + "* `" + lrLimitType + "` limit";
var lrPreview = "🚫 " + lrCustomerId + " hit their " + lrFeatureId + " limit";
var lrFields = [
{ type: "mrkdwn", text: "*Customer:*\n" + lrCustomerLink },
{ type: "mrkdwn", text: "*Feature:*\n`" + lrFeatureId + "`" },
{ type: "mrkdwn", text: "*Limit Type:*\n`" + lrLimitType + "`" }
];
if (lrEntityId) {
lrFields.push({ type: "mrkdwn", text: "*Entity:*\n`" + lrEntityId + "`" });
}
var lrContextParts = [];
if (lrCustomerId) lrContextParts.push("Customer ID: `" + lrCustomerId + "`");
if (lrEntityId) lrContextParts.push("Entity: `" + lrEntityId + "`");
var lrBlocks = [
{
type: "header",
text: { type: "plain_text", text: "🚫 Limit Reached", emoji: true }
},
{
type: "section",
text: { type: "mrkdwn", text: lrSentence }
},
{
type: "section",
fields: lrFields
}
];
if (lrCustomerId) {
lrBlocks.push({
type: "actions",
elements: [
{
type: "button",
text: { type: "plain_text", text: "View in Autumn", emoji: true },
url: AUTUMN_BASE + lrCustomerId,
style: "danger"
}
]
});
}
if (lrContextParts.length > 0) {
lrBlocks.push({
type: "context",
elements: [
{ type: "mrkdwn", text: lrContextParts.join(" | ") }
]
});
}
webhook.payload = {
username: AUTUMN_USERNAME,
icon_url: AUTUMN_ICON_URL,
text: lrPreview,
blocks: lrBlocks
};
return webhook;
}
// ============ balances.usage_alert_triggered ============
if (webhook.eventType === "balances.usage_alert_triggered") {
var uaCustomerId = data.customer_id || "";
var uaFeatureId = data.feature_id || "feature";
var uaEntityId = data.entity_id || null;
var uaAlert = data.usage_alert || {};
var uaAlertName = uaAlert.name || "Usage alert";
var uaThreshold = uaAlert.threshold;
var uaThresholdType = uaAlert.threshold_type || "usage";
function formatThreshold(value, type) {
if (value === undefined || value === null) return "—";
if (type === "usage_percentage") return value + "% used";
if (type === "remaining_percentage") return value + "% remaining";
if (type === "remaining") return value + " remaining";
return value + " used";
}
var uaThresholdLabel = formatThreshold(uaThreshold, uaThresholdType);
var uaCustomerLink = uaCustomerId
? "<" + AUTUMN_BASE + uaCustomerId + "|`" + uaCustomerId + "`>"
: "`unknown`";
var uaSentence =
uaCustomerLink + " crossed the *" + uaAlertName + "* threshold on *" + uaFeatureId + "*";
var uaPreview = "📊 Usage Alert: " + uaAlertName + " (" + uaCustomerId + ")";
var uaFields = [
{ type: "mrkdwn", text: "*Customer:*\n" + uaCustomerLink },
{ type: "mrkdwn", text: "*Feature:*\n`" + uaFeatureId + "`" },
{ type: "mrkdwn", text: "*Alert:*\n" + uaAlertName },
{ type: "mrkdwn", text: "*Threshold:*\n" + uaThresholdLabel }
];
if (uaEntityId) {
uaFields.push({ type: "mrkdwn", text: "*Entity:*\n`" + uaEntityId + "`" });
}
var uaContextParts = [];
if (uaCustomerId) uaContextParts.push("Customer ID: `" + uaCustomerId + "`");
if (uaEntityId) uaContextParts.push("Entity: `" + uaEntityId + "`");
var uaBlocks = [
{
type: "header",
text: { type: "plain_text", text: "📊 Usage Alert: " + uaAlertName, emoji: true }
},
{
type: "section",
text: { type: "mrkdwn", text: uaSentence }
},
{
type: "section",
fields: uaFields
}
];
if (uaCustomerId) {
uaBlocks.push({
type: "actions",
elements: [
{
type: "button",
text: { type: "plain_text", text: "View in Autumn", emoji: true },
url: AUTUMN_BASE + uaCustomerId
}
]
});
}
if (uaContextParts.length > 0) {
uaBlocks.push({
type: "context",
elements: [
{ type: "mrkdwn", text: uaContextParts.join(" | ") }
]
});
}
webhook.payload = {
username: AUTUMN_USERNAME,
icon_url: AUTUMN_ICON_URL,
text: uaPreview,
blocks: uaBlocks
};
return webhook;
}
// Cancel any other event types — they don't match Slack's expected schema and would error.
webhook.cancel = true;
return webhook;
}
```

View File

@@ -332,3 +332,10 @@ body {
color: #b08aff;
text-decoration-color: #b08aff;
}
/* ==========================================================================
HERO REVEAL — desktop only (lg+), driven by GSAP
On mobile, GSAP is skipped and hero elements are immediately visible.
The lg:opacity-0 utility class on .hero-reveal / .hero-cta elements
ensures they start hidden on desktop where GSAP animates them in.
========================================================================== */

View File

@@ -7,11 +7,13 @@ import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
display: "swap",
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
display: "swap",
});
const url = "https://useautumn.com";

View File

@@ -1,25 +1,27 @@
{
"lockfileVersion": 1,
"configVersion": 0,
"workspaces": {
"": {
"name": "@autumn/website",
"dependencies": {
"@gsap/react": "^2.1.2",
"@mdx-js/loader": "^3.1.1",
"@mdx-js/mdx": "^3.1.1",
"@mdx-js/react": "^3.1.1",
"@next/mdx": "^16.2.4",
"@types/react": "^19.2.14",
"@vercel/analytics": "^2.0.1",
"framer-motion": "^12.38.0",
"gray-matter": "^4.0.3",
"gsap": "^3.15.0",
"lottie-react": "^2.4.1",
"lottie-web": "^5.13.0",
"matter-js": "^0.20.0",
"motion": "^12.38.0",
"next": "16.2.4",
"next-mdx-remote": "^6.0.0",
"ngrok": "^5.0.0-beta.2",
"react": "19.2.4",
"react-dom": "19.2.4",
"react-syntax-highlighter": "^16.1.1",
"remark-frontmatter": "^5.0.0",
},
"devDependencies": {
"@tailwindcss/postcss": "^4.2.2",
@@ -159,6 +161,8 @@
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
"@mdx-js/loader": ["@mdx-js/loader@3.1.1", "", { "dependencies": { "@mdx-js/mdx": "^3.0.0", "source-map": "^0.7.0" }, "peerDependencies": { "webpack": ">=5" }, "optionalPeers": ["webpack"] }, "sha512-0TTacJyZ9mDmY+VefuthVshaNIyCGZHJG2fMnGaDttCt8HmjUF7SizlHJpaCDoGnN635nK1wpzfpx/Xx5S4WnQ=="],
"@mdx-js/mdx": ["@mdx-js/mdx@3.1.1", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "acorn": "^8.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "recma-build-jsx": "^1.0.0", "recma-jsx": "^1.0.0", "recma-stringify": "^1.0.0", "rehype-recma": "^1.0.0", "remark-mdx": "^3.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "source-map": "^0.7.0", "unified": "^11.0.0", "unist-util-position-from-estree": "^2.0.0", "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ=="],
"@mdx-js/react": ["@mdx-js/react@3.1.1", "", { "dependencies": { "@types/mdx": "^2.0.0" }, "peerDependencies": { "@types/react": ">=16", "react": ">=16" } }, "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw=="],
@@ -169,6 +173,8 @@
"@next/eslint-plugin-next": ["@next/eslint-plugin-next@16.2.1", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-r0epZGo24eT4g08jJlg2OEryBphXqO8aL18oajoTKLzHJ6jVr6P6FI58DLMug04MwD3j8Fj0YK0slyzneKVyzA=="],
"@next/mdx": ["@next/mdx@16.2.4", "", { "dependencies": { "source-map": "^0.7.0" }, "peerDependencies": { "@mdx-js/loader": ">=0.15.0", "@mdx-js/react": ">=0.15.0" }, "optionalPeers": ["@mdx-js/loader", "@mdx-js/react"] }, "sha512-e/3bgla+/oF3vDlndI0eFPa0bnP47HPVA0InsAJi7Jr3DwV8WpEGuOcm/3PdI5/93FfNiBhMVeVHZpm1sFlmJw=="],
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-OXTFFox5EKN1Ym08vfrz+OXxmCcEjT4SFMbNRsWZE99dMqt2Kcusl5MqPXcW232RYkMLQTy0hqgAMEsfEd/l2A=="],
"@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-XhpVnUfmYWvD3YrXu55XdcAkQtOnvaI6wtQa8fuF5fGoKoxIUZ0kWPtcOfqJEWngFF/lOS9l3+O9CcownhiQxQ=="],
@@ -805,10 +811,10 @@
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
"matter-js": ["matter-js@0.20.0", "", {}, "sha512-iC9fYR7zVT3HppNnsFsp9XOoQdQN2tUyfaKg4CHLH8bN+j6GT4Gw7IH2rP0tflAebrHFw730RR3DkVSZRX8hwA=="],
"mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.3", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q=="],
"mdast-util-frontmatter": ["mdast-util-frontmatter@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "escape-string-regexp": "^5.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-extension-frontmatter": "^2.0.0" } }, "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA=="],
"mdast-util-mdx": ["mdast-util-mdx@3.0.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w=="],
"mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="],
@@ -831,6 +837,8 @@
"micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="],
"micromark-extension-frontmatter": ["micromark-extension-frontmatter@2.0.0", "", { "dependencies": { "fault": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg=="],
"micromark-extension-mdx-expression": ["micromark-extension-mdx-expression@3.0.1", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q=="],
"micromark-extension-mdx-jsx": ["micromark-extension-mdx-jsx@3.0.2", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ=="],
@@ -907,8 +915,6 @@
"next": ["next@16.2.4", "", { "dependencies": { "@next/env": "16.2.4", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.2.4", "@next/swc-darwin-x64": "16.2.4", "@next/swc-linux-arm64-gnu": "16.2.4", "@next/swc-linux-arm64-musl": "16.2.4", "@next/swc-linux-x64-gnu": "16.2.4", "@next/swc-linux-x64-musl": "16.2.4", "@next/swc-win32-arm64-msvc": "16.2.4", "@next/swc-win32-x64-msvc": "16.2.4", "sharp": "^0.34.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": "dist/bin/next" }, "sha512-kPvz56wF5frc+FxlHI5qnklCzbq53HTwORaWBGdT0vNoKh1Aya9XC8aPauH4NJxqtzbWsS5mAbctm4cr+EkQ2Q=="],
"next-mdx-remote": ["next-mdx-remote@6.0.0", "", { "dependencies": { "@babel/code-frame": "^7.23.5", "@mdx-js/mdx": "^3.0.1", "@mdx-js/react": "^3.0.1", "unist-util-remove": "^4.0.0", "unist-util-visit": "^5.1.0", "vfile": "^6.0.1", "vfile-matter": "^5.0.0" }, "peerDependencies": { "react": ">=16" } }, "sha512-cJEpEZlgD6xGjB4jL8BnI8FaYdN9BzZM4NwadPe1YQr7pqoWjg9EBCMv3nXBkuHqMRfv2y33SzUsuyNh9LFAQQ=="],
"ngrok": ["ngrok@5.0.0-beta.2", "", { "dependencies": { "extract-zip": "^2.0.1", "got": "^11.8.5", "lodash.clonedeep": "^4.5.0", "uuid": "^7.0.0 || ^8.0.0", "yaml": "^2.2.2" }, "optionalDependencies": { "hpagent": "^0.1.2" }, "bin": "bin/ngrok" }, "sha512-UzsyGiJ4yTTQLCQD11k1DQaMwq2/SsztBg2b34zAqcyjS25qjDpogMKPaCKHwe/APRTHeel3iDXcVctk5CNaCQ=="],
"node-exports-info": ["node-exports-info@1.6.0", "", { "dependencies": { "array.prototype.flatmap": "^1.3.3", "es-errors": "^1.3.0", "object.entries": "^1.1.9", "semver": "^6.3.1" } }, "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw=="],
@@ -1005,6 +1011,8 @@
"rehype-recma": ["rehype-recma@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "hast-util-to-estree": "^3.0.0" } }, "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw=="],
"remark-frontmatter": ["remark-frontmatter@5.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-frontmatter": "^2.0.0", "micromark-extension-frontmatter": "^2.0.0", "unified": "^11.0.0" } }, "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ=="],
"remark-mdx": ["remark-mdx@3.1.1", "", { "dependencies": { "mdast-util-mdx": "^3.0.0", "micromark-extension-mdxjs": "^3.0.0" } }, "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg=="],
"remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="],
@@ -1137,8 +1145,6 @@
"unist-util-position-from-estree": ["unist-util-position-from-estree@2.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ=="],
"unist-util-remove": ["unist-util-remove@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg=="],
"unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="],
"unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="],
@@ -1157,8 +1163,6 @@
"vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
"vfile-matter": ["vfile-matter@5.0.1", "", { "dependencies": { "vfile": "^6.0.0", "yaml": "^2.0.0" } }, "sha512-o6roP82AiX0XfkyTHyRCMXgHfltUNlXSEqCIS80f+mbAyiQBE2fxtDVMtseyytGx75sihiJFo/zR6r/4LTs2Cw=="],
"vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
@@ -1217,6 +1221,10 @@
"is-bun-module/semver": ["semver@7.7.4", "", { "bin": "bin/semver.js" }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
"mdast-util-frontmatter/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
"micromark-extension-frontmatter/fault": ["fault@2.0.1", "", { "dependencies": { "format": "^0.2.0" } }, "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ=="],
"micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
"next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],

View File

@@ -1,14 +1,22 @@
"use client";
import gsap from "gsap";
import { forwardRef, useEffect, useImperativeHandle, useRef } from "react";
import type { PixelHoverHandle, PixelIconComponent } from "@/lib/types";
import { getGsap } from "@/lib/lazyGsap";
type GSAPTimeline = {
play: () => GSAPTimeline;
reverse: () => GSAPTimeline;
kill: () => void;
// biome-ignore lint/suspicious/noExplicitAny: structural shim for GSAP's overloaded .to() signature
to: (...args: any[]) => GSAPTimeline;
};
export const DashboardIconPixel = forwardRef<
PixelHoverHandle,
{ Icon: PixelIconComponent }
>(function DashboardIconPixel({ Icon }, ref) {
const iconRef = useRef<SVGSVGElement | null>(null);
const tlRef = useRef<gsap.core.Timeline | null>(null);
const tlRef = useRef<GSAPTimeline | null>(null);
useImperativeHandle(ref, () => ({
restart: () => tlRef.current?.play(),
@@ -16,47 +24,40 @@ export const DashboardIconPixel = forwardRef<
}));
useEffect(() => {
const pixelEls =
iconRef.current?.querySelectorAll<SVGGraphicsElement>(".icon-pixel-path");
if (!pixelEls?.length) return;
const el = iconRef.current;
if (!el) return;
let cancelled = false;
// Sort pixels by visual position: bottom-left → top-right diagonal
const pixels = Array.from(pixelEls).sort((a, b) => {
const aBox = a.getBBox();
const bBox = b.getBBox();
return (
aBox.x +
aBox.width / 2 -
(aBox.y + aBox.height / 2) -
(bBox.x + bBox.width / 2 - (bBox.y + bBox.height / 2))
);
});
getGsap().then((gsap) => {
if (cancelled) return;
const pixelEls = el.querySelectorAll<SVGGraphicsElement>(".icon-pixel-path");
if (!pixelEls.length) return;
gsap.set(pixels, {
opacity: 0.15,
scale: 0.8,
transformOrigin: "left bottom",
fill: "currentColor",
});
tlRef.current = gsap.timeline({ paused: true });
tlRef.current
.to(pixels, {
opacity: 1,
scale: 1.15,
fill: "#FFFFFF",
duration: 0.2,
stagger: 0.01,
ease: "power2.out",
})
.to(pixels, {
scale: 1,
duration: 0.01,
ease: "back.out(3)",
// Sort pixels by visual position: bottom-left → top-right diagonal
const pixels = Array.from(pixelEls).sort((a, b) => {
const aBox = a.getBBox();
const bBox = b.getBBox();
return (
aBox.x + aBox.width / 2 - (aBox.y + aBox.height / 2) -
(bBox.x + bBox.width / 2 - (bBox.y + bBox.height / 2))
);
});
gsap.set(pixels, {
opacity: 0.15,
scale: 0.8,
transformOrigin: "left bottom",
fill: "currentColor",
});
tlRef.current = gsap.timeline({ paused: true });
tlRef.current!
.to(pixels, { opacity: 1, scale: 1.15, fill: "#FFFFFF", duration: 0.2, stagger: 0.01, ease: "power2.out" })
.to(pixels, { scale: 1, duration: 0.01, ease: "back.out(3)" });
});
return () => {
cancelled = true;
tlRef.current?.kill();
};
}, []);

View File

@@ -1,5 +1,5 @@
"use client";
import { motion, useMotionValue, useSpring, useTransform } from "framer-motion";
import { motion, useMotionValue, useSpring, useTransform } from "motion/react";
import { useEffect, useRef, useState } from "react";
import type { LayoutProps } from "@/lib/types";
import AnimatedFooterImage from "./animated-footer-image";

View File

@@ -1,5 +1,5 @@
"use client";
import { AnimatePresence, motion } from "framer-motion";
import { AnimatePresence, motion } from "motion/react";
import { useState } from "react";
import { AnimatedPlusMinus, faqData } from "@/app/constant";
import { cn } from "@/lib/utils";

View File

@@ -1,13 +1,12 @@
"use client";
import { useGSAP } from "@gsap/react";
import gsap from "gsap";
import { motion } from "motion/react";
import dynamic from "next/dynamic";
import Image from "next/image";
import Link from "next/link";
import { useEffect, useRef, useState } from "react";
import { CTALines, IconCTADocs, IconCTAStart } from "@/app/constant";
import { getGsap } from "@/lib/lazyGsap";
// `AutumnConfig` pulls in `react-syntax-highlighter` + `highlight.js` (~100KB
// gzipped + meaningful parse cost on mobile). It only renders on `xl+`
@@ -88,88 +87,51 @@ export default function Hero() {
};
}, []);
useGSAP(
() => {
gsap.set(".hero-root", { opacity: 0 });
useEffect(() => {
const container = containerRef.current;
if (!container) return;
gsap.set(".hero-bg", {
opacity: 0,
filter: "blur(6px) brightness(1)",
scale: 0.97,
transformOrigin: "center top",
});
let ctx: { revert: () => void } | null = null;
let cancelled = false;
gsap.set(".hero-reveal", {
opacity: 0,
y: 25,
filter: "blur(12px)",
scale: 0.96,
transformOrigin: "center bottom",
});
// Mobile: CSS animations in globals.css handle the hero reveal.
// Guard here so mobile never initiates the ~60KB GSAP dynamic import.
if (window.innerWidth < 1024) return;
gsap.set(".hero-cta", { opacity: 0, scale: 0.95 });
getGsap().then((gsap) => {
if (cancelled) return;
ctx = gsap.context(() => {
gsap.set(".hero-reveal", {
opacity: 0,
y: 25,
scale: 0.96,
transformOrigin: "center bottom",
});
gsap.set(".hero-cta", { opacity: 0, scale: 0.95 });
const tl = gsap.timeline({
defaults: { overwrite: "auto" },
});
const tl = gsap.timeline({ defaults: { overwrite: "auto" } });
tl.to(".hero-root", { opacity: 1, duration: 0.3, ease: "none" })
// hero-bg is intentionally NOT hidden — it is the LCP element and
// must be visible from first paint. The brightness flash still runs.
tl.to(".hero-bg", { filter: "brightness(1.6)", duration: 0.125, ease: "power2.in" })
.to(".hero-bg", { filter: "brightness(1)", duration: 0.125, ease: "power2.out" })
.to(".hero-reveal", { opacity: 1, y: 0, scale: 1, duration: 1.1, stagger: 0.1, ease: "power3.out" }, "-=0.2")
.to(".hero-cta", { opacity: 1, scale: 1, duration: 0.3, stagger: 0.06, ease: "back.out(1.5)" }, "-=0.1");
}, container);
});
.to(".hero-bg", {
opacity: 1,
filter: "blur(0px) brightness(1)",
scale: 1,
duration: 0.4,
ease: "power2.out",
})
.to(".hero-bg", {
filter: "blur(0px) brightness(1.6)",
duration: 0.125,
ease: "power2.in",
})
.to(".hero-bg", {
filter: "blur(0px) brightness(1)",
duration: 0.125,
ease: "power2.out",
})
.to(
".hero-reveal",
{
opacity: 1,
y: 0,
filter: "blur(0px)",
scale: 1,
duration: 1.1,
stagger: 0.1,
ease: "power3.out",
},
"-=0.2",
)
.to(
".hero-cta",
{
opacity: 1,
scale: 1,
duration: 0.3,
stagger: 0.06,
ease: "back.out(1.5)",
},
"-=0.1",
);
},
{ scope: containerRef },
);
return () => {
cancelled = true;
ctx?.revert();
};
}, []);
return (
<div ref={containerRef}>
<div className="relative hero-root opacity-0 flex flex-col items-stretch pb-0 md:pb-12 mb-0 bg-[#0F0F0F]">
<div className="relative hero-root flex flex-col items-stretch pb-0 mb-0 bg-[#0F0F0F]">
<div className="flex justify-between">
<div className="flex flex-col gap-6 px-4 xl:px-22.75 py-8 bg-[#0F0F0F] mt-26">
<h4 className="hero-reveal relative uppercase font-mono tracking-[-2%] text-[12px] md:text-sm leading-sm text-white md:text-[#FFFFFF99] bg-[#2c2c2d] w-fit p-2 min-h-[30px] md:min-h-[36px] flex items-center">
<h4 className="hero-reveal lg:opacity-0 relative uppercase font-mono tracking-[-2%] text-[12px] md:text-sm leading-sm text-white md:text-[#FFFFFF99] bg-[#2c2c2d] w-fit p-2 min-h-[30px] md:min-h-[36px] flex items-center">
<span className="invisible select-none" aria-hidden="true">
{BADGE_TEXT}
</span>
@@ -178,13 +140,13 @@ export default function Hero() {
</span>
</h4>
<div className="flex flex-col gap-6 w-full px-0 lg:px-0">
<h1 className="hero-reveal text-[44px] md:text-[56px] w-full max-w-sm sm:max-w-[480px] md:max-w-xl leading-[44px] tracking-[-5%] md:leading-14 font-sans">
<h1 className="hero-reveal lg:opacity-0 text-[44px] md:text-[56px] w-full max-w-sm sm:max-w-[480px] md:max-w-xl leading-[44px] tracking-[-5%] md:leading-14 font-sans">
<span className="text-[#FFFFFF99] font-normal">
Drop-in credits and billing for
</span>{" "}
<span className="text-white block md:inline">AI agents</span>
</h1>
<p className="hero-reveal tracking-[-2%] w-full max-w-xs sm:max-w-[480px] md:max-w-xl text-[#FFFFFF99] md:text-[16px] text-[14px] font-light leading-5 font-sans">
<p className="hero-reveal lg:opacity-0 tracking-[-2%] w-full max-w-xs sm:max-w-[480px] md:max-w-xl text-[#FFFFFF99] md:text-[16px] text-[14px] font-light leading-5 font-sans">
Stop rebuilding usage limits, credit ledgers and payment logic.{" "}
<span className="text-white font-light">
Autumn is the customer database
@@ -201,7 +163,7 @@ export default function Hero() {
so mobile never downloads them, and desktop fills the
already-reserved space once it hydrates.
*/}
<div className="hero-reveal relative w-[50vw] max-w-[720px] min-h-[525px] p-16 py-0 mx-auto hidden xl:block">
<div className="hero-reveal lg:opacity-0 relative w-[50vw] max-w-[720px] min-h-[525px] p-16 py-0 mx-auto hidden xl:block">
{isXl && (
<>
<div className="absolute inset-0 z-0 pointer-events-none">
@@ -224,7 +186,7 @@ export default function Hero() {
<div className="border-t border-[#292929]" />
<div className="flex flex-nowrap items-center xl:px-22.75 px-4 bg-[#0F0F0F] w-full overflow-hidden">
{/* Primary CTA */}
<div className="hero-cta w-full md:w-fit md:flex-shrink-0">
<div className="hero-cta lg:opacity-0 w-full md:w-fit md:flex-shrink-0">
<Link
href={
isLoggedIn
@@ -253,7 +215,7 @@ export default function Hero() {
</div>
{/* Secondary CTA */}
<div className="hero-cta w-full md:w-fit md:flex-shrink-0">
<div className="hero-cta lg:opacity-0 w-full md:w-fit md:flex-shrink-0">
<Link href={"https://cal.com/ayrod"}>
<motion.div
initial="initial"
@@ -274,7 +236,7 @@ export default function Hero() {
</Link>
</div>
<div className="hero-cta hidden md:flex flex-nowrap gap-2 md:gap-3 ml-2 md:ml-3 h-10.5 md:h-12.5 flex-1">
<div className="hero-cta lg:opacity-0 hidden md:flex flex-nowrap gap-2 md:gap-3 ml-2 md:ml-3 h-10.5 md:h-12.5 flex-1">
<div className="border-r border-[#292929] h-full hidden md:block" />
<div className="border-r border-[#292929] h-full hidden md:block" />
<div className="border-r border-[#292929] h-full hidden md:block" />
@@ -305,11 +267,12 @@ export default function Hero() {
aria-hidden="true"
fill
priority
fetchPriority="high"
sizes="100vw"
className="hero-bg absolute inset-0 w-full h-full object-cover mix-blend-screen opacity-100 pointer-events-none select-none"
/>
<div className="hero-reveal relative z-10 w-[96%] sm:w-[90%] max-w-[520px] flex justify-center items-center">
<div className="hero-reveal lg:opacity-0 relative z-10 w-[96%] sm:w-[90%] max-w-[520px] flex justify-center items-center">
{/* <AutumnConfig lines={16} initialDelay={1000} awaitEvent="preloader:complete" /> */}
<Image
src={"/images/hero/autumn_mobile.svg"}

View File

@@ -3,11 +3,15 @@
import dynamic from "next/dynamic";
import { useEffect } from "react";
import Hero from "./hero";
import LazySection from "./lazy-section";
import SectionDivider from "./section-divider";
// All below-fold sections are code-split into separate lazy chunks so the
// initial JS bundle only contains the hero. Framer-motion, GSAP ScrollTrigger,
// and lottie-web are pulled into these chunks rather than the main bundle.
// LazySection gates each component behind an IntersectionObserver so chunks
// and their heavy assets (Lottie JSON, ScrollTrigger) only download as the
// user scrolls toward them rather than all at once on page load.
const LogoWall = dynamic(() => import("./logo-wall"));
const Problem = dynamic(() => import("./problem"));
const Solution = dynamic(() => import("./solution"));
@@ -42,32 +46,25 @@ export default function HomeSections() {
return (
<>
<Hero />
{/*
<div className="flex flex-col gap-2.5 bg-[#000000]">
<div className="border-t border-[#292929] w-full" />
<div className="border-t border-[#292929] w-full" />
<div className="border-t border-[#292929] w-full" />
<div className="border-t border-[#292929] w-full" />
<div className="border-t border-[#292929] w-full" />
</div>
<LogoWall /> */}
<LogoWall />
<SectionDivider title="THE PROBLEM" />
<Problem />
<LazySection><Problem /></LazySection>
<SectionDivider title="THE SOLUTION" />
<Solution />
<LazySection><Solution /></LazySection>
<SectionDivider title="PRICING MODELS" />
<PricingModels />
<LazySection><PricingModels /></LazySection>
<SectionDivider title="FEATURES" />
<Features />
<LazySection><Features /></LazySection>
<SectionDivider title="TESTIMONIALS" />
<Testimonials />
<LazySection><Testimonials /></LazySection>
<SectionDivider title="PRODUCTION SCALE" />
<ProductionScale />
<LazySection><ProductionScale /></LazySection>
<SectionDivider title="PRICING" />
<Pricing />
<LazySection><Pricing /></LazySection>
<SectionDivider title="FAQ" />
<FAQ />
<Footer />
<LazySection><FAQ /></LazySection>
<LazySection><Footer /></LazySection>
</>
);
}

View File

@@ -0,0 +1,37 @@
"use client";
import { useEffect, useRef, useState, type ReactNode } from "react";
export default function LazySection({ children }: { children: ReactNode }) {
const ref = useRef<HTMLDivElement>(null);
const [mounted, setMounted] = useState(false);
useEffect(() => {
const el = ref.current;
if (!el) return;
// iOS < 12.2 has no IntersectionObserver — mount everything immediately.
if (!("IntersectionObserver" in window)) {
setMounted(true);
return;
}
const observer = new IntersectionObserver(
([entry]) => {
// intersectionRatio > 0 guards against a Safari bug where
// isIntersecting fires as false on the initial sync callback.
if (entry.isIntersecting || entry.intersectionRatio > 0) {
setMounted(true);
observer.disconnect();
}
},
{ rootMargin: "1000px 0px" },
);
observer.observe(el);
return () => observer.disconnect();
}, []);
// min-height: 1px prevents the wrapper collapsing to zero height before
// content mounts, which would cause iOS to jump scroll position on mount.
return <div ref={ref} style={{ minHeight: "1px" }}>{mounted ? children : null}</div>;
}

View File

@@ -1,71 +1,101 @@
"use client";
import { useEffect, useRef } from "react";
import { cn } from "@/lib/utils";
import { getGsap } from "@/lib/lazyGsap";
const LOGOS = [
{ id: 1, name: "Mintlify", src: "/images/logos/mintlify_logo.svg.svg" },
{ id: 2, name: "Browser Use", src: "/images/logos/Browser use.svg" },
{ id: 3, name: "Firecrawl", src: "/images/logos/Firecrawl.svg.svg" },
{ id: 4, name: "Mastra", src: "/images/logos/Mastra.svg.svg" },
{ id: 5, name: "T3.chat", src: "/images/logos/T3_svg.svg" },
{ id: 6, name: "", src: null },
{ id: 1, name: "Mintlify", src: "/images/logos/mintlify_logo.svg.svg", className: "scale-90 md:scale-70" },
{ id: 3, name: "Firecrawl", src: "/images/logos/Firecrawl.svg.svg", className: "scale-95 md:scale-75 -translate-y-0.5" },
{ id: 4, name: "Mastra", src: "/images/logos/Mastra.svg.svg", className: "scale-105 md:scale-95" },
{ id: 2, name: "Browser Use", src: "/images/logos/Browser use.svg", className: "scale-85 md:scale-65" },
{ id: 5, name: "T3.chat", src: "/images/logos/T3_svg.svg", className: "scale-65 md:scale-55" },
];
const NUM_MOBILE_COLS = 2;
const NUM_DESKTOP_COLS = 3;
export default function LogoWall() {
const sectionRef = useRef<HTMLElement | null>(null);
useEffect(() => {
const section = sectionRef.current;
if (!section) return;
let ctx: { revert: () => void } | null = null;
let cancelled = false;
if (window.innerWidth < 1024) return;
getGsap().then((gsap) => {
if (cancelled) return;
ctx = gsap.context(() => {
gsap.set(".logo-wall-title", {
opacity: 0,
y: 20,
scale: 0.97,
transformOrigin: "center bottom",
});
gsap.set(".logo-wall-item", {
opacity: 0,
y: 15,
scale: 0.96,
});
const tl = gsap.timeline({
defaults: { overwrite: "auto" },
delay: 0.35,
});
tl.to(".logo-wall-title, .logo-wall-item", {
opacity: 1,
y: 0,
scale: 1,
duration: 1.1,
ease: "power3.out",
});
}, section);
});
return () => {
cancelled = true;
ctx?.revert();
};
}, []);
return (
<section className="w-full bg-[#000000]">
<div className="flex flex-col md:flex-row">
{/* Left heading — full width on mobile, 42% on desktop */}
<div className="md:w-[42%] px-4 xl:px-22.75 py-8 md:py-0 flex items-center border-b md:border-b-0 md:border-r border-[#292929]">
<div>
<p className="font-sans text-[28px] md:text-[36px] xl:text-[40px] font-normal leading-[1.1] tracking-[-0.03em]">
<span className="text-[#FFFFFF66]">Trusted by </span>
<span className="text-white">AI teams</span>
</p>
<p className="font-sans text-[28px] md:text-[36px] xl:text-[40px] font-normal leading-[1.1] tracking-[-0.03em] text-white">
shipping fast
</p>
</div>
<section
ref={sectionRef}
className="w-full bg-[#0F0F0F]"
style={{
backgroundImage:
"radial-gradient(circle, rgba(255,255,255,0.06) 1px, transparent 1px)",
backgroundSize: "14px 14px",
}}
>
<div className="flex flex-col">
<div className="px-4 xl:px-22.75 pt-10.5 flex items-center justify-center">
<span className="logo-wall-title lg:opacity-0 font-sans text-[14px] font-light text-[#FFFFFF99] tracking-[-2%] leading-5">
Powering millions of customers for growing startups
</span>
</div>
{/* Logo grid — 2 cols on mobile, 3 cols on desktop */}
<div className="flex-1 grid grid-cols-2 md:grid-cols-3">
{LOGOS.map((logo, i) => {
const isLastMobileCol = (i + 1) % NUM_MOBILE_COLS === 0;
const isLastDesktopCol = (i + 1) % NUM_DESKTOP_COLS === 0;
const isLastMobileRow = i >= LOGOS.length - NUM_MOBILE_COLS;
const isLastDesktopRow = i >= LOGOS.length - NUM_DESKTOP_COLS;
return (
<div
key={logo.id}
className={cn(
"flex items-center justify-center min-h-[90px] md:min-h-[166px] border-[#292929]",
!isLastMobileCol && "border-r",
!isLastMobileRow && "border-b",
isLastDesktopCol ? "md:border-r-0" : "md:border-r",
isLastDesktopRow ? "md:border-b-0" : "md:border-b",
)}
style={{
backgroundImage:
"radial-gradient(circle, rgba(255,255,255,0.06) 1px, transparent 1px)",
backgroundSize: "14px 14px",
}}
>
{logo.src && (
<img
src={logo.src}
alt={logo.name}
className="h-5 md:h-7 w-auto max-w-[110px] md:max-w-[150px] object-contain"
loading="lazy"
/>
)}
</div>
);
})}
<div className="flex-1 flex flex-wrap justify-center md:grid md:grid-cols-5 px-4 py-4 md:py-0">
{LOGOS.map((logo) => (
<div
key={logo.id}
className="logo-wall-item lg:opacity-0 flex items-center justify-center min-h-[50px] md:min-h-[100px] border-[#292929] w-1/3 md:w-auto"
>
{logo.src && (
<img
src={logo.src}
alt={logo.name}
className={cn(
"h-5 md:h-7 w-auto max-w-full object-contain",
logo.className,
)}
loading="lazy"
/>
)}
</div>
))}
</div>
</div>
</section>

View File

@@ -1,7 +1,5 @@
"use client";
import { useGSAP } from "@gsap/react";
import gsap from "gsap";
import { motion } from "motion/react";
import Image from "next/image";
import Link from "next/link";
@@ -29,6 +27,7 @@ import type {
PixelIconComponent,
} from "@/lib/types";
import { cn } from "@/lib/utils";
import { getGsap } from "@/lib/lazyGsap";
import { DashboardIconPixel } from "./dashboard-icon-pixel";
const NAV_LINKS = [
@@ -42,10 +41,18 @@ const NAV_LINKS = [
},
];
type GSAPTimeline = {
play: () => GSAPTimeline;
reverse: () => GSAPTimeline;
kill: () => void;
// biome-ignore lint/suspicious/noExplicitAny: structural shim for GSAP's overloaded .to() signature
to: (...args: any[]) => GSAPTimeline;
};
const NavIconPixel = forwardRef<PixelHoverHandle, { Icon: PixelIconComponent }>(
function NavIconPixel({ Icon }, ref) {
const iconRef = useRef<SVGSVGElement | null>(null);
const tlRef = useRef<gsap.core.Timeline | null>(null);
const tlRef = useRef<GSAPTimeline | null>(null);
useImperativeHandle(ref, () => ({
restart: () => tlRef.current?.play(),
@@ -53,46 +60,39 @@ const NavIconPixel = forwardRef<PixelHoverHandle, { Icon: PixelIconComponent }>(
}));
useEffect(() => {
const pixelEls =
iconRef.current?.querySelectorAll<SVGPathElement>(".icon-pixel-path");
if (!pixelEls?.length) return;
const el = iconRef.current;
if (!el) return;
let cancelled = false;
const pixels = Array.from(pixelEls).sort((a, b) => {
const aBox = a.getBBox();
const bBox = b.getBBox();
return (
aBox.x +
aBox.width / 2 -
(aBox.y + aBox.height / 2) -
(bBox.x + bBox.width / 2 - (bBox.y + bBox.height / 2))
);
});
getGsap().then((gsap) => {
if (cancelled) return;
const pixelEls = el.querySelectorAll<SVGPathElement>(".icon-pixel-path");
if (!pixelEls.length) return;
gsap.set(pixels, {
opacity: 0.15,
scale: 0.8,
transformOrigin: "left bottom",
fill: "currentColor",
});
tlRef.current = gsap.timeline({ paused: true });
tlRef.current
.to(pixels, {
opacity: 1,
scale: 1.15,
fill: "#FFFFFF",
duration: 0.01,
stagger: 0.025,
ease: "power2.out",
})
.to(pixels, {
scale: 1,
duration: 0.01,
ease: "back.out(3)",
const pixels = Array.from(pixelEls).sort((a, b) => {
const aBox = a.getBBox();
const bBox = b.getBBox();
return (
aBox.x + aBox.width / 2 - (aBox.y + aBox.height / 2) -
(bBox.x + bBox.width / 2 - (bBox.y + bBox.height / 2))
);
});
gsap.set(pixels, {
opacity: 0.15,
scale: 0.8,
transformOrigin: "left bottom",
fill: "currentColor",
});
tlRef.current = gsap.timeline({ paused: true });
tlRef.current!
.to(pixels, { opacity: 1, scale: 1.15, fill: "#FFFFFF", duration: 0.01, stagger: 0.025, ease: "power2.out" })
.to(pixels, { scale: 1, duration: 0.01, ease: "back.out(3)" });
});
return () => {
cancelled = true;
tlRef.current?.kill();
};
}, []);
@@ -189,137 +189,21 @@ export default function Navbar({
};
}, [menuOpen]);
useGSAP(
() => {
if (!animateIntro) return;
// Skip the entrance animation on non-desktop viewports, or when
// hydration landed well after first paint. On mobile the GSAP chunk
// can arrive many seconds after the server-rendered navbar has
// already painted; hiding it with `gsap.set({ opacity: 0 })` at
// that point and re-animating it in is a visible flash that's much
// worse than no animation.
if (
window.matchMedia("(max-width: 1023px)").matches ||
performance.now() > 300
) {
return;
}
gsap.set(".nav-root", { opacity: 0 });
gsap.set(".nav-logo", {
opacity: 0,
filter: "blur(6px) brightness(1)",
scale: 0.92,
transformOrigin: "left center",
});
gsap.set(".nav-link", { opacity: 0, y: -8 });
gsap.set(".nav-dashboard", { opacity: 0, scale: 0.95 });
const navMobileRef = useRef<HTMLDivElement | null>(null);
const tl = gsap.timeline({ defaults: { overwrite: "auto" } });
tl.to(".nav-root", { opacity: 1, duration: 0.3, ease: "none" })
.to(".nav-logo", {
opacity: 1,
filter: "blur(0px) brightness(1)",
scale: 1,
duration: 0.7,
ease: "power2.out",
})
.to(".nav-logo", {
filter: "blur(0px) brightness(1.6)",
duration: 0.225,
ease: "power2.in",
})
.to(".nav-logo", {
filter: "blur(0px) brightness(1)",
duration: 0.125,
ease: "power2.out",
})
.to(
".nav-link",
{
opacity: 1,
y: 0,
duration: 0.25,
stagger: 0.06,
ease: "power2.out",
},
"-=0.05",
)
.to(
".nav-dashboard",
{
opacity: 1,
scale: 1,
duration: 0.3,
ease: "back.out(1.5)",
},
"-=0.1",
);
},
{ scope: containerRef, dependencies: [animateIntro] },
);
const mobileTl = useRef<gsap.core.Timeline | null>(null);
useGSAP(
() => {
gsap.set(".nav-mobile", {
opacity: 0,
clipPath: "inset(0% 0 100% 0)",
pointerEvents: "none",
});
gsap.set(
".nav-mobile a, .nav-mobile img, .nav-mobile button, .nav-mobile .border-b",
{
opacity: 0,
filter: "blur(8px)",
scale: 0.95,
},
);
mobileTl.current = gsap.timeline({
paused: true,
defaults: { overwrite: "auto" },
});
mobileTl.current
.to(".nav-mobile", {
opacity: 1,
y: 0,
pointerEvents: "auto",
clipPath: "inset(0% 0 0% 0)",
duration: 0.2,
ease: "power3.inOut",
})
.to(
".nav-mobile a, .nav-mobile img, .nav-mobile button, .nav-mobile .border-b",
{
opacity: 1,
filter: "blur(0px)",
scale: 1,
duration: 0.15,
stagger: 0.01,
ease: "power2.out",
},
"-=0.1",
);
},
{ scope: containerRef },
);
useGSAP(
() => {
if (mobileTl.current) {
if (menuOpen) {
mobileTl.current.timeScale(1).play();
} else {
mobileTl.current.timeScale(1.8).reverse();
}
}
},
{ scope: containerRef, dependencies: [menuOpen] },
);
useEffect(() => {
const el = navMobileRef.current;
if (!el) return;
if (menuOpen) {
el.style.opacity = "1";
el.style.pointerEvents = "auto";
el.style.clipPath = "inset(0% 0 0% 0)";
} else {
el.style.opacity = "0";
el.style.pointerEvents = "none";
el.style.clipPath = "inset(0% 0 100% 0)";
}
}, [menuOpen]);
return (
<div
@@ -404,8 +288,9 @@ export default function Navbar({
</nav>
</div>
<div
ref={navMobileRef}
className={cn(
"fixed nav-mobile inset-x-0 z-40 flex h-[calc(100dvh-58px)] flex-col overflow-x-hidden overflow-y-auto bg-[#000000] px-4 pb-8 font-mono uppercase transition-all duration-300 md:px-(--page-pad) lg:top-5",
"fixed inset-x-0 z-40 flex h-[calc(100dvh-58px)] flex-col overflow-x-hidden overflow-y-auto bg-[#000000] px-4 pb-8 font-mono uppercase md:px-(--page-pad) lg:top-5",
scrolled && !recoilHidden
? "top-[58px] sm:top-[60px]"
: "top-[66px] sm:top-[62px]",
@@ -414,6 +299,7 @@ export default function Navbar({
opacity: 0,
pointerEvents: "none",
clipPath: "inset(0% 0 100% 0)",
transition: "opacity 0.2s ease, clip-path 0.2s cubic-bezier(0.87, 0, 0.13, 1)",
}}
>
{/* Nav items */}

View File

@@ -66,9 +66,16 @@ export default function SolutionAnimation() {
// Defer the 1.42.1 MB Lottie JSON fetch until the element is close to
// the viewport. Without this the browser fetches it during initial load
// and blocks the main thread while parsing the large JSON blob.
if (!("IntersectionObserver" in window)) {
initAnimation();
return;
}
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) {
// intersectionRatio > 0 guards against a Safari bug where
// isIntersecting fires as false on the initial sync callback.
if (entries[0].isIntersecting || entries[0].intersectionRatio > 0) {
observer.disconnect();
initAnimation();
}

View File

@@ -0,0 +1,10 @@
// Loads GSAP exactly once — first call triggers the dynamic import,
// subsequent calls reuse the same promise.
type Gsap = (typeof import("gsap"))["default"];
let promise: Promise<Gsap> | null = null;
export const getGsap = (): Promise<Gsap> => {
if (!promise) promise = import("gsap").then((m) => m.default);
return promise;
};

View File

@@ -7,13 +7,13 @@ const nextConfig = {
pageExtensions: ["ts", "tsx", "md", "mdx"],
allowedDevOrigins: ["*.ngrok-free.dev"],
experimental: {
optimizePackageImports: ["framer-motion", "motion", "gsap", "@gsap/react"],
optimizePackageImports: ["motion", "gsap", "@gsap/react"],
optimizeCss: true,
},
images: {
// Serve AVIF to supporting browsers (better compression than WebP),
// falling back to WebP. Next.js negotiates via Accept header automatically.
// formats: ["image/avif", "image/webp"],
formats: ["image/avif", "image/webp"],
},
async headers() {
if (!isProd) return [];

View File

@@ -16,12 +16,9 @@
"@next/mdx": "^16.2.4",
"@types/react": "^19.2.14",
"@vercel/analytics": "^2.0.1",
"framer-motion": "^12.38.0",
"gray-matter": "^4.0.3",
"gsap": "^3.15.0",
"lottie-react": "^2.4.1",
"lottie-web": "^5.13.0",
"matter-js": "^0.20.0",
"motion": "^12.38.0",
"next": "16.2.4",
"ngrok": "^5.0.0-beta.2",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 884 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 22 MiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 226 KiB

After

Width:  |  Height:  |  Size: 98 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,4 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="28" height="28" fill="white"/>
<path d="M10.7139 9.06887C9.77726 11.211 8.84052 13.3532 7.90386 15.4953C8.63795 16.4465 9.37205 17.3984 10.1061 18.3496C12.2827 15.537 14.4599 12.7244 16.637 9.91183L9.27077 22.9514C12.9161 20.7518 16.5615 18.5529 20.2069 16.3534V4.85034L10.7139 9.06887Z" fill="#8838FF"/>
</svg>

After

Width:  |  Height:  |  Size: 421 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 1.7 MiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 996 KiB

View File

@@ -26,6 +26,7 @@
"inquirer": "^12.10.0",
"knip": "^6.7.0",
"ts-to-zod": "^5.1.0",
"turbo": "^2.9.6",
},
},
"apps/checkout": {
@@ -115,12 +116,9 @@
"@next/mdx": "^16.2.4",
"@types/react": "^19.2.14",
"@vercel/analytics": "^2.0.1",
"framer-motion": "^12.38.0",
"gray-matter": "^4.0.3",
"gsap": "^3.15.0",
"lottie-react": "^2.4.1",
"lottie-web": "^5.13.0",
"matter-js": "^0.20.0",
"motion": "^12.38.0",
"next": "16.2.4",
"ngrok": "^5.0.0-beta.2",
@@ -2174,6 +2172,18 @@
"@tsconfig/node16": ["@tsconfig/node16@1.0.4", "", {}, "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA=="],
"@turbo/darwin-64": ["@turbo/darwin-64@2.9.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-X/56SnVXIQZBLKwniGTwEQTGmtE5brSACnKMBWpY3YafuxVYefrC2acamfjgxP7BG5w3I+6jf0UrLoSzgPcSJg=="],
"@turbo/darwin-arm64": ["@turbo/darwin-arm64@2.9.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-aalBeSl4agT/QtYGDyf/XLajedWzUC9Vg/pm/YO6QQ93vkQ91Vz5uK1ta5RbVRDozQSz4njxUNqRNmOXDzW+qw=="],
"@turbo/linux-64": ["@turbo/linux-64@2.9.6", "", { "os": "linux", "cpu": "x64" }, "sha512-YKi05jnNHaD7vevgYwahpzGwbsNNTwzU2c7VZdmdFm7+cGDP4oREUWSsainiMfRqjRuolQxBwRn8wf1jmu+YZA=="],
"@turbo/linux-arm64": ["@turbo/linux-arm64@2.9.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-02o/ZS69cOYEDczXvOB2xmyrtzjQ2hVFtWZK1iqxXUfzMmTjZK4UumrfNnjckSg+gqeBfnPRHa0NstA173Ik3g=="],
"@turbo/windows-64": ["@turbo/windows-64@2.9.6", "", { "os": "win32", "cpu": "x64" }, "sha512-wVdQjvnBI15wB6JrA+43CtUtagjIMmX6XYO758oZHAsCNSxqRlJtdyujih0D8OCnwCRWiGWGI63zAxR0hO6s9g=="],
"@turbo/windows-arm64": ["@turbo/windows-arm64@2.9.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-1XUUyWW0W6FTSqGEhU8RHVqb2wP1SPkr7hIvBlMEwH9jr+sJQK5kqeosLJ/QaUv4ecSAd1ZhIrLoW7qslAzT4A=="],
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
"@types/acorn": ["@types/acorn@4.0.6", "", { "dependencies": { "@types/estree": "*" } }, "sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ=="],
@@ -4228,8 +4238,6 @@
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
"matter-js": ["matter-js@0.20.0", "", {}, "sha512-iC9fYR7zVT3HppNnsFsp9XOoQdQN2tUyfaKg4CHLH8bN+j6GT4Gw7IH2rP0tflAebrHFw730RR3DkVSZRX8hwA=="],
"md-to-react-email": ["md-to-react-email@5.0.6", "", { "dependencies": { "marked": "7.0.4" }, "peerDependencies": { "react": "^18.0 || ^19.0" } }, "sha512-lKaTJqpmO88JdE3FnE7V1dtS+ta0ORyKTRhk/YoCRmeE9LozyfR2ashnUW0p0hK13VnF2TqZURXtSwgyh0zZkA=="],
"md5-hex": ["md5-hex@3.0.1", "", { "dependencies": { "blueimp-md5": "^2.10.0" } }, "sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw=="],
@@ -5446,6 +5454,8 @@
"tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="],
"turbo": ["turbo@2.9.6", "", { "optionalDependencies": { "@turbo/darwin-64": "2.9.6", "@turbo/darwin-arm64": "2.9.6", "@turbo/linux-64": "2.9.6", "@turbo/linux-arm64": "2.9.6", "@turbo/windows-64": "2.9.6", "@turbo/windows-arm64": "2.9.6" }, "bin": { "turbo": "bin/turbo" } }, "sha512-+v2QJey7ZUeUiuigkU+uFfklvNUyPI2VO2vBpMYJA+a1hKFLFiKtUYlRHdb3P9CrAvMzi0upbjI4WT+zKtqkBg=="],
"tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="],
"twoslash": ["twoslash@0.3.8", "", { "dependencies": { "@typescript/vfs": "^1.6.4", "twoslash-protocol": "0.3.8" }, "peerDependencies": { "typescript": "^5.5.0 || ^6.0.0" } }, "sha512-OeDz0kDl8sqPUN3nr7gqcvOs70f5lZsdhKYTX3/SgB9OvdadzzoYJI/4SBXhXV1HG8E9fLc+e17itoRYTxmoig=="],

View File

@@ -11,6 +11,7 @@
"enumMembers",
"duplicates"
],
"ignore": ["ai/**", "others/**"],
"ignoreWorkspaces": [
"packages/atmn",
"packages/autumn-js",
@@ -63,7 +64,12 @@
"project": ["**/*.{ts,tsx}"]
},
"apps/website": {
"entry": ["app/**/*.{ts,tsx,mdx}", "components/**/*.{ts,tsx}", "content/**/*.mdx", "*.mjs"],
"entry": [
"app/**/*.{ts,tsx,mdx}",
"components/**/*.{ts,tsx}",
"content/**/*.mdx",
"*.mjs"
],
"project": ["**/*.{ts,tsx,mdx,mjs}"]
},
"apps/checkout": {

File diff suppressed because it is too large Load Diff

View File

@@ -778,7 +778,9 @@ class Balances(BaseSDK):
)
if utils.match_response(http_res, "200", "application/json"):
return unmarshal_json_response(models.FinalizeLockResponse, http_res)
return unmarshal_json_response(models.FinalizeLockResponseBody1, http_res)
if utils.match_response(http_res, "202", "application/json"):
return unmarshal_json_response(models.FinalizeLockResponseBody2, http_res)
if utils.match_response(http_res, "4XX", "*"):
http_res_text = utils.stream_to_text(http_res)
raise errors.AutumnDefaultError(
@@ -877,7 +879,9 @@ class Balances(BaseSDK):
)
if utils.match_response(http_res, "200", "application/json"):
return unmarshal_json_response(models.FinalizeLockResponse, http_res)
return unmarshal_json_response(models.FinalizeLockResponseBody1, http_res)
if utils.match_response(http_res, "202", "application/json"):
return unmarshal_json_response(models.FinalizeLockResponseBody2, http_res)
if utils.match_response(http_res, "4XX", "*"):
http_res_text = await utils.stream_to_text_async(http_res)
raise errors.AutumnDefaultError(

View File

@@ -58,6 +58,7 @@ class Billing(BaseSDK):
] = None,
metadata: Optional[Dict[str, str]] = None,
no_billing_changes: Optional[bool] = None,
enable_plan_immediately: Optional[bool] = None,
retries: OptionalNullable[utils.RetryConfig] = UNSET,
server_url: Optional[str] = None,
timeout_ms: Optional[int] = None,
@@ -88,6 +89,7 @@ class Billing(BaseSDK):
:param carry_over_usages: Whether to carry over usages from the previous plan.
:param metadata: Key-value metadata to attach to the Stripe subscription, invoice, and checkout session created during this attach flow. Keys prefixed with 'autumn_' are reserved and will be stripped.
:param no_billing_changes: If true, skips any billing changes for the attach operation.
:param enable_plan_immediately: If true, the customer's plan is activated immediately even when payment is deferred (invoice mode) or pending (Stripe checkout). For Stripe checkout, the customer_product is inserted before the customer completes the hosted form.
:param retries: Override the default retry configuration for this method
:param server_url: Override the default server URL for this method
:param timeout_ms: Override the default request timeout configuration for this method in milliseconds
@@ -139,6 +141,7 @@ class Billing(BaseSDK):
),
metadata=metadata,
no_billing_changes=no_billing_changes,
enable_plan_immediately=enable_plan_immediately,
)
req = self._build_request(
@@ -249,6 +252,7 @@ class Billing(BaseSDK):
] = None,
metadata: Optional[Dict[str, str]] = None,
no_billing_changes: Optional[bool] = None,
enable_plan_immediately: Optional[bool] = None,
retries: OptionalNullable[utils.RetryConfig] = UNSET,
server_url: Optional[str] = None,
timeout_ms: Optional[int] = None,
@@ -279,6 +283,7 @@ class Billing(BaseSDK):
:param carry_over_usages: Whether to carry over usages from the previous plan.
:param metadata: Key-value metadata to attach to the Stripe subscription, invoice, and checkout session created during this attach flow. Keys prefixed with 'autumn_' are reserved and will be stripped.
:param no_billing_changes: If true, skips any billing changes for the attach operation.
:param enable_plan_immediately: If true, the customer's plan is activated immediately even when payment is deferred (invoice mode) or pending (Stripe checkout). For Stripe checkout, the customer_product is inserted before the customer completes the hosted form.
:param retries: Override the default retry configuration for this method
:param server_url: Override the default server URL for this method
:param timeout_ms: Override the default request timeout configuration for this method in milliseconds
@@ -330,6 +335,7 @@ class Billing(BaseSDK):
),
metadata=metadata,
no_billing_changes=no_billing_changes,
enable_plan_immediately=enable_plan_immediately,
)
req = self._build_request_async(
@@ -418,6 +424,7 @@ class Billing(BaseSDK):
checkout_session_params: Optional[Dict[str, Any]] = None,
redirect_mode: Optional[models.MultiAttachRedirectMode] = "if_required",
new_billing_subscription: Optional[bool] = None,
enable_plan_immediately: Optional[bool] = None,
customer_data: Optional[
Union[models.CustomerData, models.CustomerDataTypedDict]
] = None,
@@ -443,6 +450,7 @@ class Billing(BaseSDK):
:param checkout_session_params: Additional parameters to pass into the creation of the Stripe checkout session.
:param redirect_mode: Controls when to return a checkout URL. 'always' returns a URL even if payment succeeds, 'if_required' only when payment action is needed, 'never' disables redirects.
:param new_billing_subscription: Only applicable when the customer has an existing Stripe subscription. If true, creates a new separate subscription instead of merging into the existing one.
:param enable_plan_immediately: If true, the cusProducts are activated immediately even when payment is pending via Stripe checkout.
:param customer_data: Customer details to set when creating a customer
:param entity_data:
:param retries: Override the default retry configuration for this method
@@ -477,6 +485,7 @@ class Billing(BaseSDK):
checkout_session_params=checkout_session_params,
redirect_mode=redirect_mode,
new_billing_subscription=new_billing_subscription,
enable_plan_immediately=enable_plan_immediately,
customer_data=utils.get_pydantic_model(
customer_data, Optional[models.CustomerData]
),
@@ -571,6 +580,7 @@ class Billing(BaseSDK):
checkout_session_params: Optional[Dict[str, Any]] = None,
redirect_mode: Optional[models.MultiAttachRedirectMode] = "if_required",
new_billing_subscription: Optional[bool] = None,
enable_plan_immediately: Optional[bool] = None,
customer_data: Optional[
Union[models.CustomerData, models.CustomerDataTypedDict]
] = None,
@@ -596,6 +606,7 @@ class Billing(BaseSDK):
:param checkout_session_params: Additional parameters to pass into the creation of the Stripe checkout session.
:param redirect_mode: Controls when to return a checkout URL. 'always' returns a URL even if payment succeeds, 'if_required' only when payment action is needed, 'never' disables redirects.
:param new_billing_subscription: Only applicable when the customer has an existing Stripe subscription. If true, creates a new separate subscription instead of merging into the existing one.
:param enable_plan_immediately: If true, the cusProducts are activated immediately even when payment is pending via Stripe checkout.
:param customer_data: Customer details to set when creating a customer
:param entity_data:
:param retries: Override the default retry configuration for this method
@@ -630,6 +641,7 @@ class Billing(BaseSDK):
checkout_session_params=checkout_session_params,
redirect_mode=redirect_mode,
new_billing_subscription=new_billing_subscription,
enable_plan_immediately=enable_plan_immediately,
customer_data=utils.get_pydantic_model(
customer_data, Optional[models.CustomerData]
),
@@ -753,6 +765,7 @@ class Billing(BaseSDK):
] = None,
metadata: Optional[Dict[str, str]] = None,
no_billing_changes: Optional[bool] = None,
enable_plan_immediately: Optional[bool] = None,
retries: OptionalNullable[utils.RetryConfig] = UNSET,
server_url: Optional[str] = None,
timeout_ms: Optional[int] = None,
@@ -783,6 +796,7 @@ class Billing(BaseSDK):
:param carry_over_usages: Whether to carry over usages from the previous plan.
:param metadata: Key-value metadata to attach to the Stripe subscription, invoice, and checkout session created during this attach flow. Keys prefixed with 'autumn_' are reserved and will be stripped.
:param no_billing_changes: If true, skips any billing changes for the attach operation.
:param enable_plan_immediately: If true, the customer's plan is activated immediately even when payment is deferred (invoice mode) or pending (Stripe checkout). For Stripe checkout, the customer_product is inserted before the customer completes the hosted form.
:param retries: Override the default retry configuration for this method
:param server_url: Override the default server URL for this method
:param timeout_ms: Override the default request timeout configuration for this method in milliseconds
@@ -835,6 +849,7 @@ class Billing(BaseSDK):
),
metadata=metadata,
no_billing_changes=no_billing_changes,
enable_plan_immediately=enable_plan_immediately,
)
req = self._build_request(
@@ -952,6 +967,7 @@ class Billing(BaseSDK):
] = None,
metadata: Optional[Dict[str, str]] = None,
no_billing_changes: Optional[bool] = None,
enable_plan_immediately: Optional[bool] = None,
retries: OptionalNullable[utils.RetryConfig] = UNSET,
server_url: Optional[str] = None,
timeout_ms: Optional[int] = None,
@@ -982,6 +998,7 @@ class Billing(BaseSDK):
:param carry_over_usages: Whether to carry over usages from the previous plan.
:param metadata: Key-value metadata to attach to the Stripe subscription, invoice, and checkout session created during this attach flow. Keys prefixed with 'autumn_' are reserved and will be stripped.
:param no_billing_changes: If true, skips any billing changes for the attach operation.
:param enable_plan_immediately: If true, the customer's plan is activated immediately even when payment is deferred (invoice mode) or pending (Stripe checkout). For Stripe checkout, the customer_product is inserted before the customer completes the hosted form.
:param retries: Override the default retry configuration for this method
:param server_url: Override the default server URL for this method
:param timeout_ms: Override the default request timeout configuration for this method in milliseconds
@@ -1034,6 +1051,7 @@ class Billing(BaseSDK):
),
metadata=metadata,
no_billing_changes=no_billing_changes,
enable_plan_immediately=enable_plan_immediately,
)
req = self._build_request_async(
@@ -1126,6 +1144,7 @@ class Billing(BaseSDK):
checkout_session_params: Optional[Dict[str, Any]] = None,
redirect_mode: Optional[models.PreviewMultiAttachRedirectMode] = "if_required",
new_billing_subscription: Optional[bool] = None,
enable_plan_immediately: Optional[bool] = None,
customer_data: Optional[
Union[models.CustomerData, models.CustomerDataTypedDict]
] = None,
@@ -1154,6 +1173,7 @@ class Billing(BaseSDK):
:param checkout_session_params: Additional parameters to pass into the creation of the Stripe checkout session.
:param redirect_mode: Controls when to return a checkout URL. 'always' returns a URL even if payment succeeds, 'if_required' only when payment action is needed, 'never' disables redirects.
:param new_billing_subscription: Only applicable when the customer has an existing Stripe subscription. If true, creates a new separate subscription instead of merging into the existing one.
:param enable_plan_immediately: If true, the cusProducts are activated immediately even when payment is pending via Stripe checkout.
:param customer_data: Customer details to set when creating a customer
:param entity_data:
:param retries: Override the default retry configuration for this method
@@ -1188,6 +1208,7 @@ class Billing(BaseSDK):
checkout_session_params=checkout_session_params,
redirect_mode=redirect_mode,
new_billing_subscription=new_billing_subscription,
enable_plan_immediately=enable_plan_immediately,
customer_data=utils.get_pydantic_model(
customer_data, Optional[models.CustomerData]
),
@@ -1286,6 +1307,7 @@ class Billing(BaseSDK):
checkout_session_params: Optional[Dict[str, Any]] = None,
redirect_mode: Optional[models.PreviewMultiAttachRedirectMode] = "if_required",
new_billing_subscription: Optional[bool] = None,
enable_plan_immediately: Optional[bool] = None,
customer_data: Optional[
Union[models.CustomerData, models.CustomerDataTypedDict]
] = None,
@@ -1314,6 +1336,7 @@ class Billing(BaseSDK):
:param checkout_session_params: Additional parameters to pass into the creation of the Stripe checkout session.
:param redirect_mode: Controls when to return a checkout URL. 'always' returns a URL even if payment succeeds, 'if_required' only when payment action is needed, 'never' disables redirects.
:param new_billing_subscription: Only applicable when the customer has an existing Stripe subscription. If true, creates a new separate subscription instead of merging into the existing one.
:param enable_plan_immediately: If true, the cusProducts are activated immediately even when payment is pending via Stripe checkout.
:param customer_data: Customer details to set when creating a customer
:param entity_data:
:param retries: Override the default retry configuration for this method
@@ -1348,6 +1371,7 @@ class Billing(BaseSDK):
checkout_session_params=checkout_session_params,
redirect_mode=redirect_mode,
new_billing_subscription=new_billing_subscription,
enable_plan_immediately=enable_plan_immediately,
customer_data=utils.get_pydantic_model(
customer_data, Optional[models.CustomerData]
),
@@ -2308,6 +2332,7 @@ class Billing(BaseSDK):
] = None,
metadata: Optional[Dict[str, str]] = None,
no_billing_changes: Optional[bool] = None,
enable_plan_immediately: Optional[bool] = None,
retries: OptionalNullable[utils.RetryConfig] = UNSET,
server_url: Optional[str] = None,
timeout_ms: Optional[int] = None,
@@ -2332,6 +2357,7 @@ class Billing(BaseSDK):
:param carry_over_usages: Whether to carry over usages from the previous plan.
:param metadata: Key-value metadata to attach to the Stripe subscription, invoice, and checkout session created during this attach flow. Keys prefixed with 'autumn_' are reserved and will be stripped.
:param no_billing_changes: If true, skips any billing changes for the attach operation.
:param enable_plan_immediately: If true, the customer's plan is activated immediately even when payment is deferred (invoice mode) or pending (Stripe checkout). For Stripe checkout, the customer_product is inserted before the customer completes the hosted form.
:param retries: Override the default retry configuration for this method
:param server_url: Override the default server URL for this method
:param timeout_ms: Override the default request timeout configuration for this method in milliseconds
@@ -2377,6 +2403,7 @@ class Billing(BaseSDK):
),
metadata=metadata,
no_billing_changes=no_billing_changes,
enable_plan_immediately=enable_plan_immediately,
)
req = self._build_request(
@@ -2485,6 +2512,7 @@ class Billing(BaseSDK):
] = None,
metadata: Optional[Dict[str, str]] = None,
no_billing_changes: Optional[bool] = None,
enable_plan_immediately: Optional[bool] = None,
retries: OptionalNullable[utils.RetryConfig] = UNSET,
server_url: Optional[str] = None,
timeout_ms: Optional[int] = None,
@@ -2509,6 +2537,7 @@ class Billing(BaseSDK):
:param carry_over_usages: Whether to carry over usages from the previous plan.
:param metadata: Key-value metadata to attach to the Stripe subscription, invoice, and checkout session created during this attach flow. Keys prefixed with 'autumn_' are reserved and will be stripped.
:param no_billing_changes: If true, skips any billing changes for the attach operation.
:param enable_plan_immediately: If true, the customer's plan is activated immediately even when payment is deferred (invoice mode) or pending (Stripe checkout). For Stripe checkout, the customer_product is inserted before the customer completes the hosted form.
:param retries: Override the default retry configuration for this method
:param server_url: Override the default server URL for this method
:param timeout_ms: Override the default request timeout configuration for this method in milliseconds
@@ -2554,6 +2583,7 @@ class Billing(BaseSDK):
),
metadata=metadata,
no_billing_changes=no_billing_changes,
enable_plan_immediately=enable_plan_immediately,
)
req = self._build_request_async(

View File

@@ -158,53 +158,96 @@ if TYPE_CHECKING:
UpdateSubscriptionParamsTypedDict,
)
from .checkop import (
CheckConfig,
CheckConfigTypedDict,
CheckCreditSchema,
CheckCreditSchemaTypedDict,
CheckEnv,
CheckFeature,
CheckFeatureTypedDict,
CheckFreeTrial,
CheckFreeTrialTypedDict,
CheckConfig1,
CheckConfig1TypedDict,
CheckConfig2,
CheckConfig2TypedDict,
CheckCreditSchema1,
CheckCreditSchema1TypedDict,
CheckCreditSchema2,
CheckCreditSchema2TypedDict,
CheckEnv1,
CheckEnv2,
CheckFeature1,
CheckFeature1TypedDict,
CheckFeature2,
CheckFeature2TypedDict,
CheckFreeTrial1,
CheckFreeTrial1TypedDict,
CheckFreeTrial2,
CheckFreeTrial2TypedDict,
CheckGlobals,
CheckGlobalsTypedDict,
CheckInterval,
CheckItem,
CheckItemTypedDict,
CheckInterval1,
CheckInterval2,
CheckItem1,
CheckItem1TypedDict,
CheckItem2,
CheckItem2TypedDict,
CheckLock,
CheckLockTypedDict,
CheckOnDecrease,
CheckOnIncrease,
CheckOnDecrease1,
CheckOnDecrease2,
CheckOnIncrease1,
CheckOnIncrease2,
CheckParams,
CheckParamsTypedDict,
CheckResponse,
CheckResponseBody1,
CheckResponseBody1TypedDict,
CheckResponseBody2,
CheckResponseBody2TypedDict,
CheckResponseTypedDict,
CheckRollover,
CheckRolloverTypedDict,
CheckTierBehavior,
ConfigDuration,
FeatureType,
Flag,
FlagDisplay,
FlagDisplayTypedDict,
FlagType,
FlagTypedDict,
FreeTrialDuration,
IncludedUsage,
IncludedUsageTypedDict,
Preview,
PreviewTypedDict,
Product,
ProductDisplay,
ProductDisplayTypedDict,
ProductScenario,
ProductType,
ProductTypedDict,
Properties,
PropertiesTypedDict,
Scenario,
UsageModel,
CheckRollover1,
CheckRollover1TypedDict,
CheckRollover2,
CheckRollover2TypedDict,
CheckTierBehavior1,
CheckTierBehavior2,
ConfigDuration1,
ConfigDuration2,
FeatureType1,
FeatureType2,
Flag1,
Flag1TypedDict,
Flag2,
Flag2TypedDict,
FlagDisplay1,
FlagDisplay1TypedDict,
FlagDisplay2,
FlagDisplay2TypedDict,
FlagType1,
FlagType2,
FreeTrialDuration1,
FreeTrialDuration2,
IncludedUsage1,
IncludedUsage1TypedDict,
IncludedUsage2,
IncludedUsage2TypedDict,
Preview1,
Preview1TypedDict,
Preview2,
Preview2TypedDict,
Product1,
Product1TypedDict,
Product2,
Product2TypedDict,
ProductDisplay1,
ProductDisplay1TypedDict,
ProductDisplay2,
ProductDisplay2TypedDict,
ProductScenario1,
ProductScenario2,
ProductType1,
ProductType2,
Properties1,
Properties1TypedDict,
Properties2,
Properties2TypedDict,
Scenario1,
Scenario2,
UsageModel1,
UsageModel2,
)
from .createbalanceop import (
CreateBalanceDuration,
@@ -285,6 +328,10 @@ if TYPE_CHECKING:
CreatePlanAttachAction,
CreatePlanBillingMethodRequest,
CreatePlanBillingMethodResponse,
CreatePlanConfigRequest,
CreatePlanConfigRequestTypedDict,
CreatePlanConfigResponse,
CreatePlanConfigResponseTypedDict,
CreatePlanCreditSchema,
CreatePlanCreditSchemaTypedDict,
CreatePlanCustomerEligibility,
@@ -376,11 +423,16 @@ if TYPE_CHECKING:
CustomerFeature,
CustomerFeatureTypedDict,
CustomerFlagsType,
CustomerInterval,
CustomerInterval1,
CustomerInterval2,
CustomerOverageAllowed,
CustomerOverageAllowedTypedDict,
CustomerPurchaseLimit,
CustomerPurchaseLimitTypedDict,
CustomerPurchaseLimit1,
CustomerPurchaseLimit1TypedDict,
CustomerPurchaseLimit2,
CustomerPurchaseLimit2TypedDict,
CustomerPurchaseLimitUnion,
CustomerPurchaseLimitUnionTypedDict,
CustomerSpendLimit,
CustomerSpendLimitTypedDict,
CustomerStatus,
@@ -479,6 +531,10 @@ if TYPE_CHECKING:
FinalizeLockGlobals,
FinalizeLockGlobalsTypedDict,
FinalizeLockResponse,
FinalizeLockResponseBody1,
FinalizeLockResponseBody1TypedDict,
FinalizeLockResponseBody2,
FinalizeLockResponseBody2TypedDict,
FinalizeLockResponseTypedDict,
)
from .getentityop import (
@@ -553,6 +609,8 @@ if TYPE_CHECKING:
from .getplanop import (
GetPlanAttachAction,
GetPlanBillingMethod,
GetPlanConfig,
GetPlanConfigTypedDict,
GetPlanCreditSchema,
GetPlanCreditSchemaTypedDict,
GetPlanCustomerEligibility,
@@ -611,7 +669,8 @@ if TYPE_CHECKING:
ListCustomersFlagsTypedDict,
ListCustomersGlobals,
ListCustomersGlobalsTypedDict,
ListCustomersInterval,
ListCustomersInterval1,
ListCustomersInterval2,
ListCustomersList,
ListCustomersListTypedDict,
ListCustomersOverageAllowed,
@@ -621,8 +680,12 @@ if TYPE_CHECKING:
ListCustomersPlan,
ListCustomersPlanTypedDict,
ListCustomersPurchase,
ListCustomersPurchaseLimit,
ListCustomersPurchaseLimitTypedDict,
ListCustomersPurchaseLimit1,
ListCustomersPurchaseLimit1TypedDict,
ListCustomersPurchaseLimit2,
ListCustomersPurchaseLimit2TypedDict,
ListCustomersPurchaseLimitUnion,
ListCustomersPurchaseLimitUnionTypedDict,
ListCustomersPurchaseTypedDict,
ListCustomersResponse,
ListCustomersResponseTypedDict,
@@ -670,6 +733,8 @@ if TYPE_CHECKING:
from .listplansop import (
ListPlansAttachAction,
ListPlansBillingMethod,
ListPlansConfig,
ListPlansConfigTypedDict,
ListPlansCreditSchema,
ListPlansCreditSchemaTypedDict,
ListPlansCustomerEligibility,
@@ -793,6 +858,8 @@ if TYPE_CHECKING:
ItemTypedDict,
Plan,
PlanBillingMethod,
PlanConfig,
PlanConfigTypedDict,
PlanCreditSchema,
PlanCreditSchemaTypedDict,
PlanDurationType,
@@ -890,6 +957,9 @@ if TYPE_CHECKING:
PreviewAttachResponseTypedDict,
PreviewAttachRollover,
PreviewAttachRolloverTypedDict,
PreviewAttachStatus,
PreviewAttachTax,
PreviewAttachTaxTypedDict,
PreviewAttachTier,
PreviewAttachTierBehavior,
PreviewAttachTierTypedDict,
@@ -971,6 +1041,9 @@ if TYPE_CHECKING:
PreviewMultiAttachRolloverTypedDict,
PreviewMultiAttachSpendLimit,
PreviewMultiAttachSpendLimitTypedDict,
PreviewMultiAttachStatus,
PreviewMultiAttachTax,
PreviewMultiAttachTaxTypedDict,
PreviewMultiAttachThresholdType,
PreviewMultiAttachTier,
PreviewMultiAttachTierBehavior,
@@ -1124,6 +1197,10 @@ if TYPE_CHECKING:
TrackParams,
TrackParamsTypedDict,
TrackResponse,
TrackResponseBody1,
TrackResponseBody1TypedDict,
TrackResponseBody2,
TrackResponseBody2TypedDict,
TrackResponseTypedDict,
)
from .updatebalanceop import (
@@ -1160,7 +1237,8 @@ if TYPE_CHECKING:
UpdateCustomerGlobals,
UpdateCustomerGlobalsTypedDict,
UpdateCustomerIntervalRequest,
UpdateCustomerIntervalResponse,
UpdateCustomerIntervalResponse1,
UpdateCustomerIntervalResponse2,
UpdateCustomerOverageAllowedRequest,
UpdateCustomerOverageAllowedRequestTypedDict,
UpdateCustomerOverageAllowedResponse,
@@ -1170,8 +1248,12 @@ if TYPE_CHECKING:
UpdateCustomerPurchase,
UpdateCustomerPurchaseLimitRequest,
UpdateCustomerPurchaseLimitRequestTypedDict,
UpdateCustomerPurchaseLimitResponse,
UpdateCustomerPurchaseLimitResponseTypedDict,
UpdateCustomerPurchaseLimitResponse1,
UpdateCustomerPurchaseLimitResponse1TypedDict,
UpdateCustomerPurchaseLimitResponse2,
UpdateCustomerPurchaseLimitResponse2TypedDict,
UpdateCustomerPurchaseLimitUnion,
UpdateCustomerPurchaseLimitUnionTypedDict,
UpdateCustomerPurchaseTypedDict,
UpdateCustomerResponse,
UpdateCustomerResponseTypedDict,
@@ -1257,6 +1339,10 @@ if TYPE_CHECKING:
UpdatePlanBasePriceTypedDict,
UpdatePlanBillingMethodRequest,
UpdatePlanBillingMethodResponse,
UpdatePlanConfigRequest,
UpdatePlanConfigRequestTypedDict,
UpdatePlanConfigResponse,
UpdatePlanConfigResponseTypedDict,
UpdatePlanCreditSchema,
UpdatePlanCreditSchemaTypedDict,
UpdatePlanCustomerEligibility,
@@ -1461,32 +1547,54 @@ __all__ = [
"BinSize",
"Breakdown",
"BreakdownTypedDict",
"CheckConfig",
"CheckConfigTypedDict",
"CheckCreditSchema",
"CheckCreditSchemaTypedDict",
"CheckEnv",
"CheckFeature",
"CheckFeatureTypedDict",
"CheckFreeTrial",
"CheckFreeTrialTypedDict",
"CheckConfig1",
"CheckConfig1TypedDict",
"CheckConfig2",
"CheckConfig2TypedDict",
"CheckCreditSchema1",
"CheckCreditSchema1TypedDict",
"CheckCreditSchema2",
"CheckCreditSchema2TypedDict",
"CheckEnv1",
"CheckEnv2",
"CheckFeature1",
"CheckFeature1TypedDict",
"CheckFeature2",
"CheckFeature2TypedDict",
"CheckFreeTrial1",
"CheckFreeTrial1TypedDict",
"CheckFreeTrial2",
"CheckFreeTrial2TypedDict",
"CheckGlobals",
"CheckGlobalsTypedDict",
"CheckInterval",
"CheckItem",
"CheckItemTypedDict",
"CheckInterval1",
"CheckInterval2",
"CheckItem1",
"CheckItem1TypedDict",
"CheckItem2",
"CheckItem2TypedDict",
"CheckLock",
"CheckLockTypedDict",
"CheckOnDecrease",
"CheckOnIncrease",
"CheckOnDecrease1",
"CheckOnDecrease2",
"CheckOnIncrease1",
"CheckOnIncrease2",
"CheckParams",
"CheckParamsTypedDict",
"CheckResponse",
"CheckResponseBody1",
"CheckResponseBody1TypedDict",
"CheckResponseBody2",
"CheckResponseBody2TypedDict",
"CheckResponseTypedDict",
"CheckRollover",
"CheckRolloverTypedDict",
"CheckTierBehavior",
"ConfigDuration",
"CheckRollover1",
"CheckRollover1TypedDict",
"CheckRollover2",
"CheckRollover2TypedDict",
"CheckTierBehavior1",
"CheckTierBehavior2",
"ConfigDuration1",
"ConfigDuration2",
"CreateBalanceDuration",
"CreateBalanceGlobals",
"CreateBalanceGlobalsTypedDict",
@@ -1559,6 +1667,10 @@ __all__ = [
"CreatePlanAttachAction",
"CreatePlanBillingMethodRequest",
"CreatePlanBillingMethodResponse",
"CreatePlanConfigRequest",
"CreatePlanConfigRequestTypedDict",
"CreatePlanConfigResponse",
"CreatePlanConfigResponseTypedDict",
"CreatePlanCreditSchema",
"CreatePlanCreditSchemaTypedDict",
"CreatePlanCustomerEligibility",
@@ -1664,11 +1776,16 @@ __all__ = [
"CustomerFeature",
"CustomerFeatureTypedDict",
"CustomerFlagsType",
"CustomerInterval",
"CustomerInterval1",
"CustomerInterval2",
"CustomerOverageAllowed",
"CustomerOverageAllowedTypedDict",
"CustomerPurchaseLimit",
"CustomerPurchaseLimitTypedDict",
"CustomerPurchaseLimit1",
"CustomerPurchaseLimit1TypedDict",
"CustomerPurchaseLimit2",
"CustomerPurchaseLimit2TypedDict",
"CustomerPurchaseLimitUnion",
"CustomerPurchaseLimitUnionTypedDict",
"CustomerSpendLimit",
"CustomerSpendLimitTypedDict",
"CustomerStatus",
@@ -1717,22 +1834,33 @@ __all__ = [
"EventsListParams",
"EventsListParamsTypedDict",
"ExpiryDurationType",
"FeatureType",
"FeatureType1",
"FeatureType2",
"FinalizeBalanceParams",
"FinalizeBalanceParamsTypedDict",
"FinalizeLockGlobals",
"FinalizeLockGlobalsTypedDict",
"FinalizeLockResponse",
"FinalizeLockResponseBody1",
"FinalizeLockResponseBody1TypedDict",
"FinalizeLockResponseBody2",
"FinalizeLockResponseBody2TypedDict",
"FinalizeLockResponseTypedDict",
"Flag",
"FlagDisplay",
"FlagDisplayTypedDict",
"FlagType",
"FlagTypedDict",
"Flag1",
"Flag1TypedDict",
"Flag2",
"Flag2TypedDict",
"FlagDisplay1",
"FlagDisplay1TypedDict",
"FlagDisplay2",
"FlagDisplay2TypedDict",
"FlagType1",
"FlagType2",
"Flags",
"FlagsTypedDict",
"FreeTrial",
"FreeTrialDuration",
"FreeTrialDuration1",
"FreeTrialDuration2",
"FreeTrialRequest",
"FreeTrialRequestTypedDict",
"FreeTrialTypedDict",
@@ -1801,6 +1929,8 @@ __all__ = [
"GetOrCreateCustomerUsageAlertTypedDict",
"GetPlanAttachAction",
"GetPlanBillingMethod",
"GetPlanConfig",
"GetPlanConfigTypedDict",
"GetPlanCreditSchema",
"GetPlanCreditSchemaTypedDict",
"GetPlanCustomerEligibility",
@@ -1840,8 +1970,10 @@ __all__ = [
"GetPlanStatus",
"GetPlanTierBehavior",
"GetPlanType",
"IncludedUsage",
"IncludedUsageTypedDict",
"IncludedUsage1",
"IncludedUsage1TypedDict",
"IncludedUsage2",
"IncludedUsage2TypedDict",
"Intent",
"Interval",
"IntervalTypedDict",
@@ -1866,7 +1998,8 @@ __all__ = [
"ListCustomersFlagsTypedDict",
"ListCustomersGlobals",
"ListCustomersGlobalsTypedDict",
"ListCustomersInterval",
"ListCustomersInterval1",
"ListCustomersInterval2",
"ListCustomersList",
"ListCustomersListTypedDict",
"ListCustomersOverageAllowed",
@@ -1876,8 +2009,12 @@ __all__ = [
"ListCustomersPlan",
"ListCustomersPlanTypedDict",
"ListCustomersPurchase",
"ListCustomersPurchaseLimit",
"ListCustomersPurchaseLimitTypedDict",
"ListCustomersPurchaseLimit1",
"ListCustomersPurchaseLimit1TypedDict",
"ListCustomersPurchaseLimit2",
"ListCustomersPurchaseLimit2TypedDict",
"ListCustomersPurchaseLimitUnion",
"ListCustomersPurchaseLimitUnionTypedDict",
"ListCustomersPurchaseTypedDict",
"ListCustomersResponse",
"ListCustomersResponseTypedDict",
@@ -1915,6 +2052,8 @@ __all__ = [
"ListFeaturesType",
"ListPlansAttachAction",
"ListPlansBillingMethod",
"ListPlansConfig",
"ListPlansConfigTypedDict",
"ListPlansCreditSchema",
"ListPlansCreditSchemaTypedDict",
"ListPlansCustomerEligibility",
@@ -2024,6 +2163,8 @@ __all__ = [
"OpenCustomerPortalResponseTypedDict",
"Plan",
"PlanBillingMethod",
"PlanConfig",
"PlanConfigTypedDict",
"PlanCreditSchema",
"PlanCreditSchemaTypedDict",
"PlanDurationType",
@@ -2051,7 +2192,10 @@ __all__ = [
"PlanTierBehavior",
"PlanType",
"PlanTypedDict",
"Preview",
"Preview1",
"Preview1TypedDict",
"Preview2",
"Preview2TypedDict",
"PreviewAttachAttachDiscount",
"PreviewAttachAttachDiscountTypedDict",
"PreviewAttachBasePrice",
@@ -2120,6 +2264,9 @@ __all__ = [
"PreviewAttachResponseTypedDict",
"PreviewAttachRollover",
"PreviewAttachRolloverTypedDict",
"PreviewAttachStatus",
"PreviewAttachTax",
"PreviewAttachTaxTypedDict",
"PreviewAttachTier",
"PreviewAttachTierBehavior",
"PreviewAttachTierTypedDict",
@@ -2199,6 +2346,9 @@ __all__ = [
"PreviewMultiAttachRolloverTypedDict",
"PreviewMultiAttachSpendLimit",
"PreviewMultiAttachSpendLimitTypedDict",
"PreviewMultiAttachStatus",
"PreviewMultiAttachTax",
"PreviewMultiAttachTaxTypedDict",
"PreviewMultiAttachThresholdType",
"PreviewMultiAttachTier",
"PreviewMultiAttachTierBehavior",
@@ -2211,7 +2361,6 @@ __all__ = [
"PreviewMultiAttachUsageLineItemPeriod",
"PreviewMultiAttachUsageLineItemPeriodTypedDict",
"PreviewMultiAttachUsageLineItemTypedDict",
"PreviewTypedDict",
"PreviewUpdateAttachDiscount",
"PreviewUpdateAttachDiscountTypedDict",
"PreviewUpdateBasePrice",
@@ -2285,14 +2434,22 @@ __all__ = [
"PreviewUpdateUsageLineItemPeriodTypedDict",
"PreviewUpdateUsageLineItemTypedDict",
"Processor",
"Product",
"ProductDisplay",
"ProductDisplayTypedDict",
"ProductScenario",
"ProductType",
"ProductTypedDict",
"Properties",
"PropertiesTypedDict",
"Product1",
"Product1TypedDict",
"Product2",
"Product2TypedDict",
"ProductDisplay1",
"ProductDisplay1TypedDict",
"ProductDisplay2",
"ProductDisplay2TypedDict",
"ProductScenario1",
"ProductScenario2",
"ProductType1",
"ProductType2",
"Properties1",
"Properties1TypedDict",
"Properties2",
"Properties2TypedDict",
"Purchase",
"PurchaseTypedDict",
"Range",
@@ -2309,7 +2466,8 @@ __all__ = [
"Rewards",
"RewardsType",
"RewardsTypedDict",
"Scenario",
"Scenario1",
"Scenario2",
"Security",
"SecurityTypedDict",
"SetupPaymentAttachDiscount",
@@ -2370,6 +2528,10 @@ __all__ = [
"TrackParams",
"TrackParamsTypedDict",
"TrackResponse",
"TrackResponseBody1",
"TrackResponseBody1TypedDict",
"TrackResponseBody2",
"TrackResponseBody2TypedDict",
"TrackResponseTypedDict",
"TrialsUsed",
"TrialsUsedTypedDict",
@@ -2404,7 +2566,8 @@ __all__ = [
"UpdateCustomerGlobals",
"UpdateCustomerGlobalsTypedDict",
"UpdateCustomerIntervalRequest",
"UpdateCustomerIntervalResponse",
"UpdateCustomerIntervalResponse1",
"UpdateCustomerIntervalResponse2",
"UpdateCustomerOverageAllowedRequest",
"UpdateCustomerOverageAllowedRequestTypedDict",
"UpdateCustomerOverageAllowedResponse",
@@ -2414,8 +2577,12 @@ __all__ = [
"UpdateCustomerPurchase",
"UpdateCustomerPurchaseLimitRequest",
"UpdateCustomerPurchaseLimitRequestTypedDict",
"UpdateCustomerPurchaseLimitResponse",
"UpdateCustomerPurchaseLimitResponseTypedDict",
"UpdateCustomerPurchaseLimitResponse1",
"UpdateCustomerPurchaseLimitResponse1TypedDict",
"UpdateCustomerPurchaseLimitResponse2",
"UpdateCustomerPurchaseLimitResponse2TypedDict",
"UpdateCustomerPurchaseLimitUnion",
"UpdateCustomerPurchaseLimitUnionTypedDict",
"UpdateCustomerPurchaseTypedDict",
"UpdateCustomerResponse",
"UpdateCustomerResponseTypedDict",
@@ -2495,6 +2662,10 @@ __all__ = [
"UpdatePlanBasePriceTypedDict",
"UpdatePlanBillingMethodRequest",
"UpdatePlanBillingMethodResponse",
"UpdatePlanConfigRequest",
"UpdatePlanConfigRequestTypedDict",
"UpdatePlanConfigResponse",
"UpdatePlanConfigResponseTypedDict",
"UpdatePlanCreditSchema",
"UpdatePlanCreditSchemaTypedDict",
"UpdatePlanCustomerEligibility",
@@ -2560,7 +2731,8 @@ __all__ = [
"UpdatePlanType",
"UpdateSubscriptionParams",
"UpdateSubscriptionParamsTypedDict",
"UsageModel",
"UsageModel1",
"UsageModel2",
]
_dynamic_imports: dict[str, str] = {
@@ -2708,53 +2880,96 @@ _dynamic_imports: dict[str, str] = {
"BillingUpdateToTypedDict": ".billingupdateop",
"UpdateSubscriptionParams": ".billingupdateop",
"UpdateSubscriptionParamsTypedDict": ".billingupdateop",
"CheckConfig": ".checkop",
"CheckConfigTypedDict": ".checkop",
"CheckCreditSchema": ".checkop",
"CheckCreditSchemaTypedDict": ".checkop",
"CheckEnv": ".checkop",
"CheckFeature": ".checkop",
"CheckFeatureTypedDict": ".checkop",
"CheckFreeTrial": ".checkop",
"CheckFreeTrialTypedDict": ".checkop",
"CheckConfig1": ".checkop",
"CheckConfig1TypedDict": ".checkop",
"CheckConfig2": ".checkop",
"CheckConfig2TypedDict": ".checkop",
"CheckCreditSchema1": ".checkop",
"CheckCreditSchema1TypedDict": ".checkop",
"CheckCreditSchema2": ".checkop",
"CheckCreditSchema2TypedDict": ".checkop",
"CheckEnv1": ".checkop",
"CheckEnv2": ".checkop",
"CheckFeature1": ".checkop",
"CheckFeature1TypedDict": ".checkop",
"CheckFeature2": ".checkop",
"CheckFeature2TypedDict": ".checkop",
"CheckFreeTrial1": ".checkop",
"CheckFreeTrial1TypedDict": ".checkop",
"CheckFreeTrial2": ".checkop",
"CheckFreeTrial2TypedDict": ".checkop",
"CheckGlobals": ".checkop",
"CheckGlobalsTypedDict": ".checkop",
"CheckInterval": ".checkop",
"CheckItem": ".checkop",
"CheckItemTypedDict": ".checkop",
"CheckInterval1": ".checkop",
"CheckInterval2": ".checkop",
"CheckItem1": ".checkop",
"CheckItem1TypedDict": ".checkop",
"CheckItem2": ".checkop",
"CheckItem2TypedDict": ".checkop",
"CheckLock": ".checkop",
"CheckLockTypedDict": ".checkop",
"CheckOnDecrease": ".checkop",
"CheckOnIncrease": ".checkop",
"CheckOnDecrease1": ".checkop",
"CheckOnDecrease2": ".checkop",
"CheckOnIncrease1": ".checkop",
"CheckOnIncrease2": ".checkop",
"CheckParams": ".checkop",
"CheckParamsTypedDict": ".checkop",
"CheckResponse": ".checkop",
"CheckResponseBody1": ".checkop",
"CheckResponseBody1TypedDict": ".checkop",
"CheckResponseBody2": ".checkop",
"CheckResponseBody2TypedDict": ".checkop",
"CheckResponseTypedDict": ".checkop",
"CheckRollover": ".checkop",
"CheckRolloverTypedDict": ".checkop",
"CheckTierBehavior": ".checkop",
"ConfigDuration": ".checkop",
"FeatureType": ".checkop",
"Flag": ".checkop",
"FlagDisplay": ".checkop",
"FlagDisplayTypedDict": ".checkop",
"FlagType": ".checkop",
"FlagTypedDict": ".checkop",
"FreeTrialDuration": ".checkop",
"IncludedUsage": ".checkop",
"IncludedUsageTypedDict": ".checkop",
"Preview": ".checkop",
"PreviewTypedDict": ".checkop",
"Product": ".checkop",
"ProductDisplay": ".checkop",
"ProductDisplayTypedDict": ".checkop",
"ProductScenario": ".checkop",
"ProductType": ".checkop",
"ProductTypedDict": ".checkop",
"Properties": ".checkop",
"PropertiesTypedDict": ".checkop",
"Scenario": ".checkop",
"UsageModel": ".checkop",
"CheckRollover1": ".checkop",
"CheckRollover1TypedDict": ".checkop",
"CheckRollover2": ".checkop",
"CheckRollover2TypedDict": ".checkop",
"CheckTierBehavior1": ".checkop",
"CheckTierBehavior2": ".checkop",
"ConfigDuration1": ".checkop",
"ConfigDuration2": ".checkop",
"FeatureType1": ".checkop",
"FeatureType2": ".checkop",
"Flag1": ".checkop",
"Flag1TypedDict": ".checkop",
"Flag2": ".checkop",
"Flag2TypedDict": ".checkop",
"FlagDisplay1": ".checkop",
"FlagDisplay1TypedDict": ".checkop",
"FlagDisplay2": ".checkop",
"FlagDisplay2TypedDict": ".checkop",
"FlagType1": ".checkop",
"FlagType2": ".checkop",
"FreeTrialDuration1": ".checkop",
"FreeTrialDuration2": ".checkop",
"IncludedUsage1": ".checkop",
"IncludedUsage1TypedDict": ".checkop",
"IncludedUsage2": ".checkop",
"IncludedUsage2TypedDict": ".checkop",
"Preview1": ".checkop",
"Preview1TypedDict": ".checkop",
"Preview2": ".checkop",
"Preview2TypedDict": ".checkop",
"Product1": ".checkop",
"Product1TypedDict": ".checkop",
"Product2": ".checkop",
"Product2TypedDict": ".checkop",
"ProductDisplay1": ".checkop",
"ProductDisplay1TypedDict": ".checkop",
"ProductDisplay2": ".checkop",
"ProductDisplay2TypedDict": ".checkop",
"ProductScenario1": ".checkop",
"ProductScenario2": ".checkop",
"ProductType1": ".checkop",
"ProductType2": ".checkop",
"Properties1": ".checkop",
"Properties1TypedDict": ".checkop",
"Properties2": ".checkop",
"Properties2TypedDict": ".checkop",
"Scenario1": ".checkop",
"Scenario2": ".checkop",
"UsageModel1": ".checkop",
"UsageModel2": ".checkop",
"CreateBalanceDuration": ".createbalanceop",
"CreateBalanceGlobals": ".createbalanceop",
"CreateBalanceGlobalsTypedDict": ".createbalanceop",
@@ -2827,6 +3042,10 @@ _dynamic_imports: dict[str, str] = {
"CreatePlanAttachAction": ".createplanop",
"CreatePlanBillingMethodRequest": ".createplanop",
"CreatePlanBillingMethodResponse": ".createplanop",
"CreatePlanConfigRequest": ".createplanop",
"CreatePlanConfigRequestTypedDict": ".createplanop",
"CreatePlanConfigResponse": ".createplanop",
"CreatePlanConfigResponseTypedDict": ".createplanop",
"CreatePlanCreditSchema": ".createplanop",
"CreatePlanCreditSchemaTypedDict": ".createplanop",
"CreatePlanCustomerEligibility": ".createplanop",
@@ -2914,11 +3133,16 @@ _dynamic_imports: dict[str, str] = {
"CustomerFeature": ".customer",
"CustomerFeatureTypedDict": ".customer",
"CustomerFlagsType": ".customer",
"CustomerInterval": ".customer",
"CustomerInterval1": ".customer",
"CustomerInterval2": ".customer",
"CustomerOverageAllowed": ".customer",
"CustomerOverageAllowedTypedDict": ".customer",
"CustomerPurchaseLimit": ".customer",
"CustomerPurchaseLimitTypedDict": ".customer",
"CustomerPurchaseLimit1": ".customer",
"CustomerPurchaseLimit1TypedDict": ".customer",
"CustomerPurchaseLimit2": ".customer",
"CustomerPurchaseLimit2TypedDict": ".customer",
"CustomerPurchaseLimitUnion": ".customer",
"CustomerPurchaseLimitUnionTypedDict": ".customer",
"CustomerSpendLimit": ".customer",
"CustomerSpendLimitTypedDict": ".customer",
"CustomerStatus": ".customer",
@@ -3003,6 +3227,10 @@ _dynamic_imports: dict[str, str] = {
"FinalizeLockGlobals": ".finalizelockop",
"FinalizeLockGlobalsTypedDict": ".finalizelockop",
"FinalizeLockResponse": ".finalizelockop",
"FinalizeLockResponseBody1": ".finalizelockop",
"FinalizeLockResponseBody1TypedDict": ".finalizelockop",
"FinalizeLockResponseBody2": ".finalizelockop",
"FinalizeLockResponseBody2TypedDict": ".finalizelockop",
"FinalizeLockResponseTypedDict": ".finalizelockop",
"GetEntityBillingControls": ".getentityop",
"GetEntityBillingControlsTypedDict": ".getentityop",
@@ -3069,6 +3297,8 @@ _dynamic_imports: dict[str, str] = {
"GetOrCreateCustomerUsageAlertTypedDict": ".getorcreatecustomerop",
"GetPlanAttachAction": ".getplanop",
"GetPlanBillingMethod": ".getplanop",
"GetPlanConfig": ".getplanop",
"GetPlanConfigTypedDict": ".getplanop",
"GetPlanCreditSchema": ".getplanop",
"GetPlanCreditSchemaTypedDict": ".getplanop",
"GetPlanCustomerEligibility": ".getplanop",
@@ -3125,7 +3355,8 @@ _dynamic_imports: dict[str, str] = {
"ListCustomersFlagsTypedDict": ".listcustomersop",
"ListCustomersGlobals": ".listcustomersop",
"ListCustomersGlobalsTypedDict": ".listcustomersop",
"ListCustomersInterval": ".listcustomersop",
"ListCustomersInterval1": ".listcustomersop",
"ListCustomersInterval2": ".listcustomersop",
"ListCustomersList": ".listcustomersop",
"ListCustomersListTypedDict": ".listcustomersop",
"ListCustomersOverageAllowed": ".listcustomersop",
@@ -3135,8 +3366,12 @@ _dynamic_imports: dict[str, str] = {
"ListCustomersPlan": ".listcustomersop",
"ListCustomersPlanTypedDict": ".listcustomersop",
"ListCustomersPurchase": ".listcustomersop",
"ListCustomersPurchaseLimit": ".listcustomersop",
"ListCustomersPurchaseLimitTypedDict": ".listcustomersop",
"ListCustomersPurchaseLimit1": ".listcustomersop",
"ListCustomersPurchaseLimit1TypedDict": ".listcustomersop",
"ListCustomersPurchaseLimit2": ".listcustomersop",
"ListCustomersPurchaseLimit2TypedDict": ".listcustomersop",
"ListCustomersPurchaseLimitUnion": ".listcustomersop",
"ListCustomersPurchaseLimitUnionTypedDict": ".listcustomersop",
"ListCustomersPurchaseTypedDict": ".listcustomersop",
"ListCustomersResponse": ".listcustomersop",
"ListCustomersResponseTypedDict": ".listcustomersop",
@@ -3178,6 +3413,8 @@ _dynamic_imports: dict[str, str] = {
"ListFeaturesType": ".listfeaturesop",
"ListPlansAttachAction": ".listplansop",
"ListPlansBillingMethod": ".listplansop",
"ListPlansConfig": ".listplansop",
"ListPlansConfigTypedDict": ".listplansop",
"ListPlansCreditSchema": ".listplansop",
"ListPlansCreditSchemaTypedDict": ".listplansop",
"ListPlansCustomerEligibility": ".listplansop",
@@ -3295,6 +3532,8 @@ _dynamic_imports: dict[str, str] = {
"ItemTypedDict": ".plan",
"Plan": ".plan",
"PlanBillingMethod": ".plan",
"PlanConfig": ".plan",
"PlanConfigTypedDict": ".plan",
"PlanCreditSchema": ".plan",
"PlanCreditSchemaTypedDict": ".plan",
"PlanDurationType": ".plan",
@@ -3390,6 +3629,9 @@ _dynamic_imports: dict[str, str] = {
"PreviewAttachResponseTypedDict": ".previewattachop",
"PreviewAttachRollover": ".previewattachop",
"PreviewAttachRolloverTypedDict": ".previewattachop",
"PreviewAttachStatus": ".previewattachop",
"PreviewAttachTax": ".previewattachop",
"PreviewAttachTaxTypedDict": ".previewattachop",
"PreviewAttachTier": ".previewattachop",
"PreviewAttachTierBehavior": ".previewattachop",
"PreviewAttachTierTypedDict": ".previewattachop",
@@ -3469,6 +3711,9 @@ _dynamic_imports: dict[str, str] = {
"PreviewMultiAttachRolloverTypedDict": ".previewmultiattachop",
"PreviewMultiAttachSpendLimit": ".previewmultiattachop",
"PreviewMultiAttachSpendLimitTypedDict": ".previewmultiattachop",
"PreviewMultiAttachStatus": ".previewmultiattachop",
"PreviewMultiAttachTax": ".previewmultiattachop",
"PreviewMultiAttachTaxTypedDict": ".previewmultiattachop",
"PreviewMultiAttachThresholdType": ".previewmultiattachop",
"PreviewMultiAttachTier": ".previewmultiattachop",
"PreviewMultiAttachTierBehavior": ".previewmultiattachop",
@@ -3615,6 +3860,10 @@ _dynamic_imports: dict[str, str] = {
"TrackParams": ".trackop",
"TrackParamsTypedDict": ".trackop",
"TrackResponse": ".trackop",
"TrackResponseBody1": ".trackop",
"TrackResponseBody1TypedDict": ".trackop",
"TrackResponseBody2": ".trackop",
"TrackResponseBody2TypedDict": ".trackop",
"TrackResponseTypedDict": ".trackop",
"UpdateBalanceGlobals": ".updatebalanceop",
"UpdateBalanceGlobalsTypedDict": ".updatebalanceop",
@@ -3647,7 +3896,8 @@ _dynamic_imports: dict[str, str] = {
"UpdateCustomerGlobals": ".updatecustomerop",
"UpdateCustomerGlobalsTypedDict": ".updatecustomerop",
"UpdateCustomerIntervalRequest": ".updatecustomerop",
"UpdateCustomerIntervalResponse": ".updatecustomerop",
"UpdateCustomerIntervalResponse1": ".updatecustomerop",
"UpdateCustomerIntervalResponse2": ".updatecustomerop",
"UpdateCustomerOverageAllowedRequest": ".updatecustomerop",
"UpdateCustomerOverageAllowedRequestTypedDict": ".updatecustomerop",
"UpdateCustomerOverageAllowedResponse": ".updatecustomerop",
@@ -3657,8 +3907,12 @@ _dynamic_imports: dict[str, str] = {
"UpdateCustomerPurchase": ".updatecustomerop",
"UpdateCustomerPurchaseLimitRequest": ".updatecustomerop",
"UpdateCustomerPurchaseLimitRequestTypedDict": ".updatecustomerop",
"UpdateCustomerPurchaseLimitResponse": ".updatecustomerop",
"UpdateCustomerPurchaseLimitResponseTypedDict": ".updatecustomerop",
"UpdateCustomerPurchaseLimitResponse1": ".updatecustomerop",
"UpdateCustomerPurchaseLimitResponse1TypedDict": ".updatecustomerop",
"UpdateCustomerPurchaseLimitResponse2": ".updatecustomerop",
"UpdateCustomerPurchaseLimitResponse2TypedDict": ".updatecustomerop",
"UpdateCustomerPurchaseLimitUnion": ".updatecustomerop",
"UpdateCustomerPurchaseLimitUnionTypedDict": ".updatecustomerop",
"UpdateCustomerPurchaseTypedDict": ".updatecustomerop",
"UpdateCustomerResponse": ".updatecustomerop",
"UpdateCustomerResponseTypedDict": ".updatecustomerop",
@@ -3738,6 +3992,10 @@ _dynamic_imports: dict[str, str] = {
"UpdatePlanBasePriceTypedDict": ".updateplanop",
"UpdatePlanBillingMethodRequest": ".updateplanop",
"UpdatePlanBillingMethodResponse": ".updateplanop",
"UpdatePlanConfigRequest": ".updateplanop",
"UpdatePlanConfigRequestTypedDict": ".updateplanop",
"UpdatePlanConfigResponse": ".updateplanop",
"UpdatePlanConfigResponseTypedDict": ".updateplanop",
"UpdatePlanCreditSchema": ".updateplanop",
"UpdatePlanCreditSchemaTypedDict": ".updateplanop",
"UpdatePlanCustomerEligibility": ".updateplanop",

View File

@@ -793,6 +793,8 @@ class AttachParamsTypedDict(TypedDict):
r"""Key-value metadata to attach to the Stripe subscription, invoice, and checkout session created during this attach flow. Keys prefixed with 'autumn_' are reserved and will be stripped."""
no_billing_changes: NotRequired[bool]
r"""If true, skips any billing changes for the attach operation."""
enable_plan_immediately: NotRequired[bool]
r"""If true, the customer's plan is activated immediately even when payment is deferred (invoice mode) or pending (Stripe checkout). For Stripe checkout, the customer_product is inserted before the customer completes the hosted form."""
class AttachParams(BaseModel):
@@ -865,6 +867,9 @@ class AttachParams(BaseModel):
no_billing_changes: Optional[bool] = None
r"""If true, skips any billing changes for the attach operation."""
enable_plan_immediately: Optional[bool] = None
r"""If true, the customer's plan is activated immediately even when payment is deferred (invoice mode) or pending (Stripe checkout). For Stripe checkout, the customer_product is inserted before the customer completes the hosted form."""
@model_serializer(mode="wrap")
def serialize_model(self, handler):
optional_fields = set(
@@ -889,6 +894,7 @@ class AttachParams(BaseModel):
"carry_over_usages",
"metadata",
"no_billing_changes",
"enable_plan_immediately",
]
)
serialized = handler(self)

File diff suppressed because it is too large Load Diff

View File

@@ -476,6 +476,36 @@ class FreeTrialRequest(BaseModel):
return m
class CreatePlanConfigRequestTypedDict(TypedDict):
r"""Miscellaneous plan-level configuration flags."""
ignore_past_due: NotRequired[bool]
r"""If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state."""
class CreatePlanConfigRequest(BaseModel):
r"""Miscellaneous plan-level configuration flags."""
ignore_past_due: Optional[bool] = False
r"""If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state."""
@model_serializer(mode="wrap")
def serialize_model(self, handler):
optional_fields = set(["ignore_past_due"])
serialized = handler(self)
m = {}
for n, f in type(self).model_fields.items():
k = f.alias or n
val = serialized.get(k, serialized.get(n))
if val != UNSET_SENTINEL:
if val is not None or k not in optional_fields:
m[k] = val
return m
class CreatePlanParamsTypedDict(TypedDict):
plan_id: str
r"""The ID of the plan to create."""
@@ -495,6 +525,8 @@ class CreatePlanParamsTypedDict(TypedDict):
r"""Feature configurations for this plan. Each item defines included units, pricing, and reset behavior."""
free_trial: NotRequired[FreeTrialRequestTypedDict]
r"""Free trial configuration. Customers can try this plan before being charged."""
config: NotRequired[CreatePlanConfigRequestTypedDict]
r"""Miscellaneous plan-level configuration flags."""
class CreatePlanParams(BaseModel):
@@ -525,6 +557,9 @@ class CreatePlanParams(BaseModel):
free_trial: Optional[FreeTrialRequest] = None
r"""Free trial configuration. Customers can try this plan before being charged."""
config: Optional[CreatePlanConfigRequest] = None
r"""Miscellaneous plan-level configuration flags."""
@model_serializer(mode="wrap")
def serialize_model(self, handler):
optional_fields = set(
@@ -536,6 +571,7 @@ class CreatePlanParams(BaseModel):
"price",
"items",
"free_trial",
"config",
]
)
nullable_fields = set(["description"])
@@ -1121,6 +1157,36 @@ CreatePlanEnv = Union[
r"""Environment this plan belongs to ('sandbox' or 'live')."""
class CreatePlanConfigResponseTypedDict(TypedDict):
r"""Miscellaneous plan-level configuration flags."""
ignore_past_due: NotRequired[bool]
r"""If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state."""
class CreatePlanConfigResponse(BaseModel):
r"""Miscellaneous plan-level configuration flags."""
ignore_past_due: Optional[bool] = False
r"""If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state."""
@model_serializer(mode="wrap")
def serialize_model(self, handler):
optional_fields = set(["ignore_past_due"])
serialized = handler(self)
m = {}
for n, f in type(self).model_fields.items():
k = f.alias or n
val = serialized.get(k, serialized.get(n))
if val != UNSET_SENTINEL:
if val is not None or k not in optional_fields:
m[k] = val
return m
CreatePlanStatus = Union[
Literal[
"active",
@@ -1219,6 +1285,8 @@ class CreatePlanResponseTypedDict(TypedDict):
r"""Whether the plan is archived. Archived plans cannot be attached to new customers."""
base_variant_id: Nullable[str]
r"""If this is a variant, the ID of the base plan it was created from."""
config: CreatePlanConfigResponseTypedDict
r"""Miscellaneous plan-level configuration flags."""
free_trial: NotRequired[CreatePlanFreeTrialResponseTypedDict]
r"""Free trial configuration. If set, new customers can try this plan before being charged."""
customer_eligibility: NotRequired[CreatePlanCustomerEligibilityTypedDict]
@@ -1266,6 +1334,9 @@ class CreatePlanResponse(BaseModel):
base_variant_id: Nullable[str]
r"""If this is a variant, the ID of the base plan it was created from."""
config: CreatePlanConfigResponse
r"""Miscellaneous plan-level configuration flags."""
free_trial: Optional[CreatePlanFreeTrialResponse] = None
r"""Free trial configuration. If set, new customers can try this plan before being charged."""

View File

@@ -13,7 +13,7 @@ from autumn_sdk.types import (
)
from pydantic import model_serializer
from typing import Any, Dict, List, Literal, Optional, Union
from typing_extensions import NotRequired, TypedDict
from typing_extensions import NotRequired, TypeAliasType, TypedDict
CustomerEnv = Union[
@@ -26,7 +26,62 @@ CustomerEnv = Union[
r"""The environment this customer was created in."""
CustomerInterval = Union[
CustomerInterval2 = Union[
Literal[
"hour",
"day",
"week",
"month",
],
UnrecognizedStr,
]
class CustomerPurchaseLimit2TypedDict(TypedDict):
interval: Nullable[CustomerInterval2]
r"""The time interval for the purchase limit window. Null when no purchase limit is configured."""
interval_count: Nullable[float]
r"""Number of intervals in the purchase limit window. Null when no purchase limit is configured."""
limit: Nullable[float]
r"""Maximum number of auto top-ups allowed within the interval. Null when no purchase limit is configured."""
count: float
r"""Number of auto top-ups already consumed in the current window."""
next_reset_at: float
r"""Unix ms timestamp when the current purchase window ends and the count resets."""
class CustomerPurchaseLimit2(BaseModel):
interval: Nullable[CustomerInterval2]
r"""The time interval for the purchase limit window. Null when no purchase limit is configured."""
interval_count: Nullable[float]
r"""Number of intervals in the purchase limit window. Null when no purchase limit is configured."""
limit: Nullable[float]
r"""Maximum number of auto top-ups allowed within the interval. Null when no purchase limit is configured."""
count: float
r"""Number of auto top-ups already consumed in the current window."""
next_reset_at: float
r"""Unix ms timestamp when the current purchase window ends and the count resets."""
@model_serializer(mode="wrap")
def serialize_model(self, handler):
serialized = handler(self)
m = {}
for n, f in type(self).model_fields.items():
k = f.alias or n
val = serialized.get(k, serialized.get(n))
if val != UNSET_SENTINEL:
m[k] = val
return m
CustomerInterval1 = Union[
Literal[
"hour",
"day",
@@ -38,10 +93,8 @@ CustomerInterval = Union[
r"""The time interval for the purchase limit window."""
class CustomerPurchaseLimitTypedDict(TypedDict):
r"""Optional rate limit to cap how often auto top-ups occur."""
interval: CustomerInterval
class CustomerPurchaseLimit1TypedDict(TypedDict):
interval: CustomerInterval1
r"""The time interval for the purchase limit window."""
limit: float
r"""Maximum number of auto top-ups allowed within the interval."""
@@ -49,10 +102,8 @@ class CustomerPurchaseLimitTypedDict(TypedDict):
r"""Number of intervals in the purchase limit window."""
class CustomerPurchaseLimit(BaseModel):
r"""Optional rate limit to cap how often auto top-ups occur."""
interval: CustomerInterval
class CustomerPurchaseLimit1(BaseModel):
interval: CustomerInterval1
r"""The time interval for the purchase limit window."""
limit: float
@@ -78,6 +129,19 @@ class CustomerPurchaseLimit(BaseModel):
return m
CustomerPurchaseLimitUnionTypedDict = TypeAliasType(
"CustomerPurchaseLimitUnionTypedDict",
Union[CustomerPurchaseLimit1TypedDict, CustomerPurchaseLimit2TypedDict],
)
r"""Optional rate limit to cap how often auto top-ups occur. Expand billing_controls.auto_topups.purchase_limit for a count of top ups and the next_reset_at."""
CustomerPurchaseLimitUnion = TypeAliasType(
"CustomerPurchaseLimitUnion", Union[CustomerPurchaseLimit1, CustomerPurchaseLimit2]
)
r"""Optional rate limit to cap how often auto top-ups occur. Expand billing_controls.auto_topups.purchase_limit for a count of top ups and the next_reset_at."""
class CustomerAutoTopupTypedDict(TypedDict):
feature_id: str
r"""The ID of the feature (credit balance) to auto top-up."""
@@ -87,8 +151,8 @@ class CustomerAutoTopupTypedDict(TypedDict):
r"""Amount of credits to add per auto top-up."""
enabled: NotRequired[bool]
r"""Whether auto top-up is enabled."""
purchase_limit: NotRequired[CustomerPurchaseLimitTypedDict]
r"""Optional rate limit to cap how often auto top-ups occur."""
purchase_limit: NotRequired[CustomerPurchaseLimitUnionTypedDict]
r"""Optional rate limit to cap how often auto top-ups occur. Expand billing_controls.auto_topups.purchase_limit for a count of top ups and the next_reset_at."""
invoice_mode: NotRequired[bool]
r"""When true, auto top-up creates a send_invoice invoice instead of auto-charging."""
@@ -106,8 +170,8 @@ class CustomerAutoTopup(BaseModel):
enabled: Optional[bool] = False
r"""Whether auto top-up is enabled."""
purchase_limit: Optional[CustomerPurchaseLimit] = None
r"""Optional rate limit to cap how often auto top-ups occur."""
purchase_limit: Optional[CustomerPurchaseLimitUnion] = None
r"""Optional rate limit to cap how often auto top-ups occur. Expand billing_controls.auto_topups.purchase_limit for a count of top ups and the next_reset_at."""
invoice_mode: Optional[bool] = None
r"""When true, auto top-up creates a send_invoice invoice instead of auto-charging."""

View File

@@ -5,8 +5,8 @@ from autumn_sdk.types import BaseModel, UNSET_SENTINEL
from autumn_sdk.utils import FieldMetadata, HeaderMetadata
import pydantic
from pydantic import model_serializer
from typing import Any, Dict, Literal, Optional
from typing_extensions import Annotated, NotRequired, TypedDict
from typing import Any, Dict, Literal, Optional, Union
from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict
class FinalizeLockGlobalsTypedDict(TypedDict):
@@ -85,13 +85,36 @@ class FinalizeBalanceParams(BaseModel):
return m
class FinalizeLockResponseTypedDict(TypedDict):
class FinalizeLockResponseBody2TypedDict(TypedDict):
r"""Accepted. Autumn is experiencing degraded service from a downstream provider, so the finalize request was allowed fail-open."""
success: bool
class FinalizeLockResponseBody2(BaseModel):
r"""Accepted. Autumn is experiencing degraded service from a downstream provider, so the finalize request was allowed fail-open."""
success: bool
class FinalizeLockResponseBody1TypedDict(TypedDict):
r"""OK"""
success: bool
class FinalizeLockResponse(BaseModel):
class FinalizeLockResponseBody1(BaseModel):
r"""OK"""
success: bool
FinalizeLockResponseTypedDict = TypeAliasType(
"FinalizeLockResponseTypedDict",
Union[FinalizeLockResponseBody1TypedDict, FinalizeLockResponseBody2TypedDict],
)
FinalizeLockResponse = TypeAliasType(
"FinalizeLockResponse", Union[FinalizeLockResponseBody1, FinalizeLockResponseBody2]
)

View File

@@ -635,6 +635,36 @@ GetPlanEnv = Union[
r"""Environment this plan belongs to ('sandbox' or 'live')."""
class GetPlanConfigTypedDict(TypedDict):
r"""Miscellaneous plan-level configuration flags."""
ignore_past_due: NotRequired[bool]
r"""If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state."""
class GetPlanConfig(BaseModel):
r"""Miscellaneous plan-level configuration flags."""
ignore_past_due: Optional[bool] = False
r"""If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state."""
@model_serializer(mode="wrap")
def serialize_model(self, handler):
optional_fields = set(["ignore_past_due"])
serialized = handler(self)
m = {}
for n, f in type(self).model_fields.items():
k = f.alias or n
val = serialized.get(k, serialized.get(n))
if val != UNSET_SENTINEL:
if val is not None or k not in optional_fields:
m[k] = val
return m
GetPlanStatus = Union[
Literal[
"active",
@@ -733,6 +763,8 @@ class GetPlanResponseTypedDict(TypedDict):
r"""Whether the plan is archived. Archived plans cannot be attached to new customers."""
base_variant_id: Nullable[str]
r"""If this is a variant, the ID of the base plan it was created from."""
config: GetPlanConfigTypedDict
r"""Miscellaneous plan-level configuration flags."""
free_trial: NotRequired[GetPlanFreeTrialTypedDict]
r"""Free trial configuration. If set, new customers can try this plan before being charged."""
customer_eligibility: NotRequired[GetPlanCustomerEligibilityTypedDict]
@@ -780,6 +812,9 @@ class GetPlanResponse(BaseModel):
base_variant_id: Nullable[str]
r"""If this is a variant, the ID of the base plan it was created from."""
config: GetPlanConfig
r"""Miscellaneous plan-level configuration flags."""
free_trial: Optional[GetPlanFreeTrial] = None
r"""Free trial configuration. If set, new customers can try this plan before being charged."""

View File

@@ -15,7 +15,7 @@ from autumn_sdk.utils import FieldMetadata, HeaderMetadata
import pydantic
from pydantic import model_serializer
from typing import Any, Dict, List, Literal, Optional, Union
from typing_extensions import Annotated, NotRequired, TypedDict
from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict
class ListCustomersGlobalsTypedDict(TypedDict):
@@ -150,7 +150,62 @@ ListCustomersEnv = Union[
r"""The environment this customer was created in."""
ListCustomersInterval = Union[
ListCustomersInterval2 = Union[
Literal[
"hour",
"day",
"week",
"month",
],
UnrecognizedStr,
]
class ListCustomersPurchaseLimit2TypedDict(TypedDict):
interval: Nullable[ListCustomersInterval2]
r"""The time interval for the purchase limit window. Null when no purchase limit is configured."""
interval_count: Nullable[float]
r"""Number of intervals in the purchase limit window. Null when no purchase limit is configured."""
limit: Nullable[float]
r"""Maximum number of auto top-ups allowed within the interval. Null when no purchase limit is configured."""
count: float
r"""Number of auto top-ups already consumed in the current window."""
next_reset_at: float
r"""Unix ms timestamp when the current purchase window ends and the count resets."""
class ListCustomersPurchaseLimit2(BaseModel):
interval: Nullable[ListCustomersInterval2]
r"""The time interval for the purchase limit window. Null when no purchase limit is configured."""
interval_count: Nullable[float]
r"""Number of intervals in the purchase limit window. Null when no purchase limit is configured."""
limit: Nullable[float]
r"""Maximum number of auto top-ups allowed within the interval. Null when no purchase limit is configured."""
count: float
r"""Number of auto top-ups already consumed in the current window."""
next_reset_at: float
r"""Unix ms timestamp when the current purchase window ends and the count resets."""
@model_serializer(mode="wrap")
def serialize_model(self, handler):
serialized = handler(self)
m = {}
for n, f in type(self).model_fields.items():
k = f.alias or n
val = serialized.get(k, serialized.get(n))
if val != UNSET_SENTINEL:
m[k] = val
return m
ListCustomersInterval1 = Union[
Literal[
"hour",
"day",
@@ -162,10 +217,8 @@ ListCustomersInterval = Union[
r"""The time interval for the purchase limit window."""
class ListCustomersPurchaseLimitTypedDict(TypedDict):
r"""Optional rate limit to cap how often auto top-ups occur."""
interval: ListCustomersInterval
class ListCustomersPurchaseLimit1TypedDict(TypedDict):
interval: ListCustomersInterval1
r"""The time interval for the purchase limit window."""
limit: float
r"""Maximum number of auto top-ups allowed within the interval."""
@@ -173,10 +226,8 @@ class ListCustomersPurchaseLimitTypedDict(TypedDict):
r"""Number of intervals in the purchase limit window."""
class ListCustomersPurchaseLimit(BaseModel):
r"""Optional rate limit to cap how often auto top-ups occur."""
interval: ListCustomersInterval
class ListCustomersPurchaseLimit1(BaseModel):
interval: ListCustomersInterval1
r"""The time interval for the purchase limit window."""
limit: float
@@ -202,6 +253,20 @@ class ListCustomersPurchaseLimit(BaseModel):
return m
ListCustomersPurchaseLimitUnionTypedDict = TypeAliasType(
"ListCustomersPurchaseLimitUnionTypedDict",
Union[ListCustomersPurchaseLimit1TypedDict, ListCustomersPurchaseLimit2TypedDict],
)
r"""Optional rate limit to cap how often auto top-ups occur. Expand billing_controls.auto_topups.purchase_limit for a count of top ups and the next_reset_at."""
ListCustomersPurchaseLimitUnion = TypeAliasType(
"ListCustomersPurchaseLimitUnion",
Union[ListCustomersPurchaseLimit1, ListCustomersPurchaseLimit2],
)
r"""Optional rate limit to cap how often auto top-ups occur. Expand billing_controls.auto_topups.purchase_limit for a count of top ups and the next_reset_at."""
class ListCustomersAutoTopupTypedDict(TypedDict):
feature_id: str
r"""The ID of the feature (credit balance) to auto top-up."""
@@ -211,8 +276,8 @@ class ListCustomersAutoTopupTypedDict(TypedDict):
r"""Amount of credits to add per auto top-up."""
enabled: NotRequired[bool]
r"""Whether auto top-up is enabled."""
purchase_limit: NotRequired[ListCustomersPurchaseLimitTypedDict]
r"""Optional rate limit to cap how often auto top-ups occur."""
purchase_limit: NotRequired[ListCustomersPurchaseLimitUnionTypedDict]
r"""Optional rate limit to cap how often auto top-ups occur. Expand billing_controls.auto_topups.purchase_limit for a count of top ups and the next_reset_at."""
invoice_mode: NotRequired[bool]
r"""When true, auto top-up creates a send_invoice invoice instead of auto-charging."""
@@ -230,8 +295,8 @@ class ListCustomersAutoTopup(BaseModel):
enabled: Optional[bool] = False
r"""Whether auto top-up is enabled."""
purchase_limit: Optional[ListCustomersPurchaseLimit] = None
r"""Optional rate limit to cap how often auto top-ups occur."""
purchase_limit: Optional[ListCustomersPurchaseLimitUnion] = None
r"""Optional rate limit to cap how often auto top-ups occur. Expand billing_controls.auto_topups.purchase_limit for a count of top ups and the next_reset_at."""
invoice_mode: Optional[bool] = None
r"""When true, auto top-up creates a send_invoice invoice instead of auto-charging."""

View File

@@ -640,6 +640,36 @@ ListPlansEnv = Union[
r"""Environment this plan belongs to ('sandbox' or 'live')."""
class ListPlansConfigTypedDict(TypedDict):
r"""Miscellaneous plan-level configuration flags."""
ignore_past_due: NotRequired[bool]
r"""If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state."""
class ListPlansConfig(BaseModel):
r"""Miscellaneous plan-level configuration flags."""
ignore_past_due: Optional[bool] = False
r"""If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state."""
@model_serializer(mode="wrap")
def serialize_model(self, handler):
optional_fields = set(["ignore_past_due"])
serialized = handler(self)
m = {}
for n, f in type(self).model_fields.items():
k = f.alias or n
val = serialized.get(k, serialized.get(n))
if val != UNSET_SENTINEL:
if val is not None or k not in optional_fields:
m[k] = val
return m
ListPlansStatus = Union[
Literal[
"active",
@@ -738,6 +768,8 @@ class ListPlansListTypedDict(TypedDict):
r"""Whether the plan is archived. Archived plans cannot be attached to new customers."""
base_variant_id: Nullable[str]
r"""If this is a variant, the ID of the base plan it was created from."""
config: ListPlansConfigTypedDict
r"""Miscellaneous plan-level configuration flags."""
free_trial: NotRequired[ListPlansFreeTrialTypedDict]
r"""Free trial configuration. If set, new customers can try this plan before being charged."""
customer_eligibility: NotRequired[ListPlansCustomerEligibilityTypedDict]
@@ -785,6 +817,9 @@ class ListPlansList(BaseModel):
base_variant_id: Nullable[str]
r"""If this is a variant, the ID of the base plan it was created from."""
config: ListPlansConfig
r"""Miscellaneous plan-level configuration flags."""
free_trial: Optional[ListPlansFreeTrial] = None
r"""Free trial configuration. If set, new customers can try this plan before being charged."""

View File

@@ -911,6 +911,8 @@ class MultiAttachParamsTypedDict(TypedDict):
r"""Controls when to return a checkout URL. 'always' returns a URL even if payment succeeds, 'if_required' only when payment action is needed, 'never' disables redirects."""
new_billing_subscription: NotRequired[bool]
r"""Only applicable when the customer has an existing Stripe subscription. If true, creates a new separate subscription instead of merging into the existing one."""
enable_plan_immediately: NotRequired[bool]
r"""If true, the cusProducts are activated immediately even when payment is pending via Stripe checkout."""
customer_data: NotRequired[CustomerDataTypedDict]
r"""Customer details to set when creating a customer"""
entity_data: NotRequired[MultiAttachEntityDataTypedDict]
@@ -947,6 +949,9 @@ class MultiAttachParams(BaseModel):
new_billing_subscription: Optional[bool] = None
r"""Only applicable when the customer has an existing Stripe subscription. If true, creates a new separate subscription instead of merging into the existing one."""
enable_plan_immediately: Optional[bool] = None
r"""If true, the cusProducts are activated immediately even when payment is pending via Stripe checkout."""
customer_data: Optional[CustomerData] = None
r"""Customer details to set when creating a customer"""
@@ -964,6 +969,7 @@ class MultiAttachParams(BaseModel):
"checkout_session_params",
"redirect_mode",
"new_billing_subscription",
"enable_plan_immediately",
"customer_data",
"entity_data",
]

View File

@@ -574,6 +574,36 @@ PlanEnv = Union[
r"""Environment this plan belongs to ('sandbox' or 'live')."""
class PlanConfigTypedDict(TypedDict):
r"""Miscellaneous plan-level configuration flags."""
ignore_past_due: NotRequired[bool]
r"""If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state."""
class PlanConfig(BaseModel):
r"""Miscellaneous plan-level configuration flags."""
ignore_past_due: Optional[bool] = False
r"""If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state."""
@model_serializer(mode="wrap")
def serialize_model(self, handler):
optional_fields = set(["ignore_past_due"])
serialized = handler(self)
m = {}
for n, f in type(self).model_fields.items():
k = f.alias or n
val = serialized.get(k, serialized.get(n))
if val != UNSET_SENTINEL:
if val is not None or k not in optional_fields:
m[k] = val
return m
PlanStatus = Union[
Literal[
"active",
@@ -670,6 +700,8 @@ class PlanTypedDict(TypedDict):
r"""Whether the plan is archived. Archived plans cannot be attached to new customers."""
base_variant_id: Nullable[str]
r"""If this is a variant, the ID of the base plan it was created from."""
config: PlanConfigTypedDict
r"""Miscellaneous plan-level configuration flags."""
free_trial: NotRequired[FreeTrialTypedDict]
r"""Free trial configuration. If set, new customers can try this plan before being charged."""
customer_eligibility: NotRequired[CustomerEligibilityTypedDict]
@@ -715,6 +747,9 @@ class Plan(BaseModel):
base_variant_id: Nullable[str]
r"""If this is a variant, the ID of the base plan it was created from."""
config: PlanConfig
r"""Miscellaneous plan-level configuration flags."""
free_trial: Optional[FreeTrial] = None
r"""Free trial configuration. If set, new customers can try this plan before being charged."""

View File

@@ -794,6 +794,8 @@ class PreviewAttachParamsTypedDict(TypedDict):
r"""Key-value metadata to attach to the Stripe subscription, invoice, and checkout session created during this attach flow. Keys prefixed with 'autumn_' are reserved and will be stripped."""
no_billing_changes: NotRequired[bool]
r"""If true, skips any billing changes for the attach operation."""
enable_plan_immediately: NotRequired[bool]
r"""If true, the customer's plan is activated immediately even when payment is deferred (invoice mode) or pending (Stripe checkout). For Stripe checkout, the customer_product is inserted before the customer completes the hosted form."""
class PreviewAttachParams(BaseModel):
@@ -866,6 +868,9 @@ class PreviewAttachParams(BaseModel):
no_billing_changes: Optional[bool] = None
r"""If true, skips any billing changes for the attach operation."""
enable_plan_immediately: Optional[bool] = None
r"""If true, the customer's plan is activated immediately even when payment is deferred (invoice mode) or pending (Stripe checkout). For Stripe checkout, the customer_product is inserted before the customer completes the hosted form."""
@model_serializer(mode="wrap")
def serialize_model(self, handler):
optional_fields = set(
@@ -890,6 +895,7 @@ class PreviewAttachParams(BaseModel):
"carry_over_usages",
"metadata",
"no_billing_changes",
"enable_plan_immediately",
]
)
serialized = handler(self)
@@ -1418,6 +1424,50 @@ PreviewAttachCheckoutType = Union[
]
PreviewAttachStatus = Union[
Literal[
"complete",
"incomplete",
],
UnrecognizedStr,
]
r"""Calculation status ('complete' when Stripe Tax succeeds or 'incomplete' when Stripe Tax returned 0 or errored)."""
class PreviewAttachTaxTypedDict(TypedDict):
r"""Tax preview for the immediate charge. Contact us to enable the tax flag on your organisation. Shows only with flag enabled, a Stripe customer exists and has a location."""
total: float
r"""Total tax amount in major currency units."""
amount_inclusive: float
r"""Tax included in line item subtotals."""
amount_exclusive: float
r"""Tax added on top of subtotals."""
currency: str
r"""Three-letter currency code."""
status: PreviewAttachStatus
r"""Calculation status ('complete' when Stripe Tax succeeds or 'incomplete' when Stripe Tax returned 0 or errored)."""
class PreviewAttachTax(BaseModel):
r"""Tax preview for the immediate charge. Contact us to enable the tax flag on your organisation. Shows only with flag enabled, a Stripe customer exists and has a location."""
total: float
r"""Total tax amount in major currency units."""
amount_inclusive: float
r"""Tax included in line item subtotals."""
amount_exclusive: float
r"""Tax added on top of subtotals."""
currency: str
r"""Three-letter currency code."""
status: PreviewAttachStatus
r"""Calculation status ('complete' when Stripe Tax succeeds or 'incomplete' when Stripe Tax returned 0 or errored)."""
class PreviewAttachResponseTypedDict(TypedDict):
r"""OK"""
@@ -1442,6 +1492,8 @@ class PreviewAttachResponseTypedDict(TypedDict):
r"""Preview of the next billing cycle, if applicable. This shows what the customer will be charged in subsequent cycles."""
expand: NotRequired[List[str]]
r"""Expand the response with additional data."""
tax: NotRequired[PreviewAttachTaxTypedDict]
r"""Tax preview for the immediate charge. Contact us to enable the tax flag on your organisation. Shows only with flag enabled, a Stripe customer exists and has a location."""
class PreviewAttachResponse(BaseModel):
@@ -1479,9 +1531,12 @@ class PreviewAttachResponse(BaseModel):
expand: Optional[List[str]] = None
r"""Expand the response with additional data."""
tax: Optional[PreviewAttachTax] = None
r"""Tax preview for the immediate charge. Contact us to enable the tax flag on your organisation. Shows only with flag enabled, a Stripe customer exists and has a location."""
@model_serializer(mode="wrap")
def serialize_model(self, handler):
optional_fields = set(["next_cycle", "expand"])
optional_fields = set(["next_cycle", "expand", "tax"])
nullable_fields = set(["checkout_type"])
serialized = handler(self)
m = {}

View File

@@ -916,6 +916,8 @@ class PreviewMultiAttachParamsTypedDict(TypedDict):
r"""Controls when to return a checkout URL. 'always' returns a URL even if payment succeeds, 'if_required' only when payment action is needed, 'never' disables redirects."""
new_billing_subscription: NotRequired[bool]
r"""Only applicable when the customer has an existing Stripe subscription. If true, creates a new separate subscription instead of merging into the existing one."""
enable_plan_immediately: NotRequired[bool]
r"""If true, the cusProducts are activated immediately even when payment is pending via Stripe checkout."""
customer_data: NotRequired[CustomerDataTypedDict]
r"""Customer details to set when creating a customer"""
entity_data: NotRequired[PreviewMultiAttachEntityDataTypedDict]
@@ -952,6 +954,9 @@ class PreviewMultiAttachParams(BaseModel):
new_billing_subscription: Optional[bool] = None
r"""Only applicable when the customer has an existing Stripe subscription. If true, creates a new separate subscription instead of merging into the existing one."""
enable_plan_immediately: Optional[bool] = None
r"""If true, the cusProducts are activated immediately even when payment is pending via Stripe checkout."""
customer_data: Optional[CustomerData] = None
r"""Customer details to set when creating a customer"""
@@ -969,6 +974,7 @@ class PreviewMultiAttachParams(BaseModel):
"checkout_session_params",
"redirect_mode",
"new_billing_subscription",
"enable_plan_immediately",
"customer_data",
"entity_data",
]
@@ -1508,6 +1514,50 @@ PreviewMultiAttachCheckoutType = Union[
]
PreviewMultiAttachStatus = Union[
Literal[
"complete",
"incomplete",
],
UnrecognizedStr,
]
r"""Calculation status ('complete' when Stripe Tax succeeds or 'incomplete' when Stripe Tax returned 0 or errored)."""
class PreviewMultiAttachTaxTypedDict(TypedDict):
r"""Tax preview for the immediate charge. Contact us to enable the tax flag on your organisation. Shows only with flag enabled, a Stripe customer exists and has a location."""
total: float
r"""Total tax amount in major currency units."""
amount_inclusive: float
r"""Tax included in line item subtotals."""
amount_exclusive: float
r"""Tax added on top of subtotals."""
currency: str
r"""Three-letter currency code."""
status: PreviewMultiAttachStatus
r"""Calculation status ('complete' when Stripe Tax succeeds or 'incomplete' when Stripe Tax returned 0 or errored)."""
class PreviewMultiAttachTax(BaseModel):
r"""Tax preview for the immediate charge. Contact us to enable the tax flag on your organisation. Shows only with flag enabled, a Stripe customer exists and has a location."""
total: float
r"""Total tax amount in major currency units."""
amount_inclusive: float
r"""Tax included in line item subtotals."""
amount_exclusive: float
r"""Tax added on top of subtotals."""
currency: str
r"""Three-letter currency code."""
status: PreviewMultiAttachStatus
r"""Calculation status ('complete' when Stripe Tax succeeds or 'incomplete' when Stripe Tax returned 0 or errored)."""
class PreviewMultiAttachResponseTypedDict(TypedDict):
r"""OK"""
@@ -1532,6 +1582,8 @@ class PreviewMultiAttachResponseTypedDict(TypedDict):
r"""Preview of the next billing cycle, if applicable. This shows what the customer will be charged in subsequent cycles."""
expand: NotRequired[List[str]]
r"""Expand the response with additional data."""
tax: NotRequired[PreviewMultiAttachTaxTypedDict]
r"""Tax preview for the immediate charge. Contact us to enable the tax flag on your organisation. Shows only with flag enabled, a Stripe customer exists and has a location."""
class PreviewMultiAttachResponse(BaseModel):
@@ -1569,9 +1621,12 @@ class PreviewMultiAttachResponse(BaseModel):
expand: Optional[List[str]] = None
r"""Expand the response with additional data."""
tax: Optional[PreviewMultiAttachTax] = None
r"""Tax preview for the immediate charge. Contact us to enable the tax flag on your organisation. Shows only with flag enabled, a Stripe customer exists and has a location."""
@model_serializer(mode="wrap")
def serialize_model(self, handler):
optional_fields = set(["next_cycle", "expand"])
optional_fields = set(["next_cycle", "expand", "tax"])
nullable_fields = set(["checkout_type"])
serialized = handler(self)
m = {}

View File

@@ -729,6 +729,8 @@ class SetupPaymentParamsTypedDict(TypedDict):
r"""Key-value metadata to attach to the Stripe subscription, invoice, and checkout session created during this attach flow. Keys prefixed with 'autumn_' are reserved and will be stripped."""
no_billing_changes: NotRequired[bool]
r"""If true, skips any billing changes for the attach operation."""
enable_plan_immediately: NotRequired[bool]
r"""If true, the customer's plan is activated immediately even when payment is deferred (invoice mode) or pending (Stripe checkout). For Stripe checkout, the customer_product is inserted before the customer completes the hosted form."""
class SetupPaymentParams(BaseModel):
@@ -789,6 +791,9 @@ class SetupPaymentParams(BaseModel):
no_billing_changes: Optional[bool] = None
r"""If true, skips any billing changes for the attach operation."""
enable_plan_immediately: Optional[bool] = None
r"""If true, the customer's plan is activated immediately even when payment is deferred (invoice mode) or pending (Stripe checkout). For Stripe checkout, the customer_product is inserted before the customer completes the hosted form."""
@model_serializer(mode="wrap")
def serialize_model(self, handler):
optional_fields = set(
@@ -810,6 +815,7 @@ class SetupPaymentParams(BaseModel):
"carry_over_usages",
"metadata",
"no_billing_changes",
"enable_plan_immediately",
]
)
serialized = handler(self)

View File

@@ -7,8 +7,8 @@ from autumn_sdk.utils import FieldMetadata, HeaderMetadata, validate_const
import pydantic
from pydantic import model_serializer
from pydantic.functional_validators import AfterValidator
from typing import Any, Dict, Literal, Optional
from typing_extensions import Annotated, NotRequired, TypedDict
from typing import Any, Dict, Literal, Optional, Union
from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict
class TrackGlobalsTypedDict(TypedDict):
@@ -134,7 +134,71 @@ class TrackParams(BaseModel):
return m
class TrackResponseTypedDict(TypedDict):
class TrackResponseBody2TypedDict(TypedDict):
r"""Accepted. Autumn is experiencing degraded service from a downstream provider, so the event was accepted for replay and will be tracked as soon as the service is restored."""
customer_id: str
r"""The ID of the customer whose usage was tracked."""
value: float
r"""The amount of usage that was recorded."""
balance: Nullable[BalanceTypedDict]
r"""The updated balance for the tracked feature. Null if tracking by event_name that affects multiple features."""
entity_id: NotRequired[str]
r"""The ID of the entity, if entity-scoped tracking was performed."""
event_name: NotRequired[str]
r"""The event name that was tracked, if event_name was used instead of feature_id."""
balances: NotRequired[Dict[str, BalanceTypedDict]]
r"""Map of feature_id to updated balance when tracking by event_name affects multiple features."""
class TrackResponseBody2(BaseModel):
r"""Accepted. Autumn is experiencing degraded service from a downstream provider, so the event was accepted for replay and will be tracked as soon as the service is restored."""
customer_id: str
r"""The ID of the customer whose usage was tracked."""
value: float
r"""The amount of usage that was recorded."""
balance: Nullable[Balance]
r"""The updated balance for the tracked feature. Null if tracking by event_name that affects multiple features."""
entity_id: Optional[str] = None
r"""The ID of the entity, if entity-scoped tracking was performed."""
event_name: Optional[str] = None
r"""The event name that was tracked, if event_name was used instead of feature_id."""
balances: Optional[Dict[str, Balance]] = None
r"""Map of feature_id to updated balance when tracking by event_name affects multiple features."""
@model_serializer(mode="wrap")
def serialize_model(self, handler):
optional_fields = set(["entity_id", "event_name", "balances"])
nullable_fields = set(["balance"])
serialized = handler(self)
m = {}
for n, f in type(self).model_fields.items():
k = f.alias or n
val = serialized.get(k, serialized.get(n))
is_nullable_and_explicitly_set = (
k in nullable_fields
and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
)
if val != UNSET_SENTINEL:
if (
val is not None
or k not in optional_fields
or is_nullable_and_explicitly_set
):
m[k] = val
return m
class TrackResponseBody1TypedDict(TypedDict):
r"""OK"""
customer_id: str
@@ -151,7 +215,7 @@ class TrackResponseTypedDict(TypedDict):
r"""Map of feature_id to updated balance when tracking by event_name affects multiple features."""
class TrackResponse(BaseModel):
class TrackResponseBody1(BaseModel):
r"""OK"""
customer_id: str
@@ -198,6 +262,17 @@ class TrackResponse(BaseModel):
return m
TrackResponseTypedDict = TypeAliasType(
"TrackResponseTypedDict",
Union[TrackResponseBody1TypedDict, TrackResponseBody2TypedDict],
)
TrackResponse = TypeAliasType(
"TrackResponse", Union[TrackResponseBody1, TrackResponseBody2]
)
try:
TrackLock.model_rebuild()
except NameError:

View File

@@ -15,7 +15,7 @@ from autumn_sdk.utils import FieldMetadata, HeaderMetadata
import pydantic
from pydantic import model_serializer
from typing import Any, Dict, List, Literal, Optional, Union
from typing_extensions import Annotated, NotRequired, TypedDict
from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict
class UpdateCustomerGlobalsTypedDict(TypedDict):
@@ -447,7 +447,62 @@ UpdateCustomerEnv = Union[
r"""The environment this customer was created in."""
UpdateCustomerIntervalResponse = Union[
UpdateCustomerIntervalResponse2 = Union[
Literal[
"hour",
"day",
"week",
"month",
],
UnrecognizedStr,
]
class UpdateCustomerPurchaseLimitResponse2TypedDict(TypedDict):
interval: Nullable[UpdateCustomerIntervalResponse2]
r"""The time interval for the purchase limit window. Null when no purchase limit is configured."""
interval_count: Nullable[float]
r"""Number of intervals in the purchase limit window. Null when no purchase limit is configured."""
limit: Nullable[float]
r"""Maximum number of auto top-ups allowed within the interval. Null when no purchase limit is configured."""
count: float
r"""Number of auto top-ups already consumed in the current window."""
next_reset_at: float
r"""Unix ms timestamp when the current purchase window ends and the count resets."""
class UpdateCustomerPurchaseLimitResponse2(BaseModel):
interval: Nullable[UpdateCustomerIntervalResponse2]
r"""The time interval for the purchase limit window. Null when no purchase limit is configured."""
interval_count: Nullable[float]
r"""Number of intervals in the purchase limit window. Null when no purchase limit is configured."""
limit: Nullable[float]
r"""Maximum number of auto top-ups allowed within the interval. Null when no purchase limit is configured."""
count: float
r"""Number of auto top-ups already consumed in the current window."""
next_reset_at: float
r"""Unix ms timestamp when the current purchase window ends and the count resets."""
@model_serializer(mode="wrap")
def serialize_model(self, handler):
serialized = handler(self)
m = {}
for n, f in type(self).model_fields.items():
k = f.alias or n
val = serialized.get(k, serialized.get(n))
if val != UNSET_SENTINEL:
m[k] = val
return m
UpdateCustomerIntervalResponse1 = Union[
Literal[
"hour",
"day",
@@ -459,10 +514,8 @@ UpdateCustomerIntervalResponse = Union[
r"""The time interval for the purchase limit window."""
class UpdateCustomerPurchaseLimitResponseTypedDict(TypedDict):
r"""Optional rate limit to cap how often auto top-ups occur."""
interval: UpdateCustomerIntervalResponse
class UpdateCustomerPurchaseLimitResponse1TypedDict(TypedDict):
interval: UpdateCustomerIntervalResponse1
r"""The time interval for the purchase limit window."""
limit: float
r"""Maximum number of auto top-ups allowed within the interval."""
@@ -470,10 +523,8 @@ class UpdateCustomerPurchaseLimitResponseTypedDict(TypedDict):
r"""Number of intervals in the purchase limit window."""
class UpdateCustomerPurchaseLimitResponse(BaseModel):
r"""Optional rate limit to cap how often auto top-ups occur."""
interval: UpdateCustomerIntervalResponse
class UpdateCustomerPurchaseLimitResponse1(BaseModel):
interval: UpdateCustomerIntervalResponse1
r"""The time interval for the purchase limit window."""
limit: float
@@ -499,6 +550,23 @@ class UpdateCustomerPurchaseLimitResponse(BaseModel):
return m
UpdateCustomerPurchaseLimitUnionTypedDict = TypeAliasType(
"UpdateCustomerPurchaseLimitUnionTypedDict",
Union[
UpdateCustomerPurchaseLimitResponse1TypedDict,
UpdateCustomerPurchaseLimitResponse2TypedDict,
],
)
r"""Optional rate limit to cap how often auto top-ups occur. Expand billing_controls.auto_topups.purchase_limit for a count of top ups and the next_reset_at."""
UpdateCustomerPurchaseLimitUnion = TypeAliasType(
"UpdateCustomerPurchaseLimitUnion",
Union[UpdateCustomerPurchaseLimitResponse1, UpdateCustomerPurchaseLimitResponse2],
)
r"""Optional rate limit to cap how often auto top-ups occur. Expand billing_controls.auto_topups.purchase_limit for a count of top ups and the next_reset_at."""
class UpdateCustomerAutoTopupResponseTypedDict(TypedDict):
feature_id: str
r"""The ID of the feature (credit balance) to auto top-up."""
@@ -508,8 +576,8 @@ class UpdateCustomerAutoTopupResponseTypedDict(TypedDict):
r"""Amount of credits to add per auto top-up."""
enabled: NotRequired[bool]
r"""Whether auto top-up is enabled."""
purchase_limit: NotRequired[UpdateCustomerPurchaseLimitResponseTypedDict]
r"""Optional rate limit to cap how often auto top-ups occur."""
purchase_limit: NotRequired[UpdateCustomerPurchaseLimitUnionTypedDict]
r"""Optional rate limit to cap how often auto top-ups occur. Expand billing_controls.auto_topups.purchase_limit for a count of top ups and the next_reset_at."""
invoice_mode: NotRequired[bool]
r"""When true, auto top-up creates a send_invoice invoice instead of auto-charging."""
@@ -527,8 +595,8 @@ class UpdateCustomerAutoTopupResponse(BaseModel):
enabled: Optional[bool] = False
r"""Whether auto top-up is enabled."""
purchase_limit: Optional[UpdateCustomerPurchaseLimitResponse] = None
r"""Optional rate limit to cap how often auto top-ups occur."""
purchase_limit: Optional[UpdateCustomerPurchaseLimitUnion] = None
r"""Optional rate limit to cap how often auto top-ups occur. Expand billing_controls.auto_topups.purchase_limit for a count of top ups and the next_reset_at."""
invoice_mode: Optional[bool] = None
r"""When true, auto top-up creates a send_invoice invoice instead of auto-charging."""

View File

@@ -476,6 +476,36 @@ class UpdatePlanFreeTrialParams(BaseModel):
return m
class UpdatePlanConfigRequestTypedDict(TypedDict):
r"""Miscellaneous plan-level configuration flags."""
ignore_past_due: NotRequired[bool]
r"""If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state."""
class UpdatePlanConfigRequest(BaseModel):
r"""Miscellaneous plan-level configuration flags."""
ignore_past_due: Optional[bool] = False
r"""If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state."""
@model_serializer(mode="wrap")
def serialize_model(self, handler):
optional_fields = set(["ignore_past_due"])
serialized = handler(self)
m = {}
for n, f in type(self).model_fields.items():
k = f.alias or n
val = serialized.get(k, serialized.get(n))
if val != UNSET_SENTINEL:
if val is not None or k not in optional_fields:
m[k] = val
return m
class UpdatePlanParamsTypedDict(TypedDict):
plan_id: str
r"""The ID of the plan to update."""
@@ -494,6 +524,8 @@ class UpdatePlanParamsTypedDict(TypedDict):
r"""Feature configurations for this plan. Each item defines included units, pricing, and reset behavior."""
free_trial: NotRequired[Nullable[UpdatePlanFreeTrialParamsTypedDict]]
r"""The free trial of the plan. Set to null to remove the free trial."""
config: NotRequired[UpdatePlanConfigRequestTypedDict]
r"""Miscellaneous plan-level configuration flags."""
version: NotRequired[float]
archived: NotRequired[bool]
new_plan_id: NotRequired[str]
@@ -527,6 +559,9 @@ class UpdatePlanParams(BaseModel):
free_trial: OptionalNullable[UpdatePlanFreeTrialParams] = UNSET
r"""The free trial of the plan. Set to null to remove the free trial."""
config: Optional[UpdatePlanConfigRequest] = None
r"""Miscellaneous plan-level configuration flags."""
version: Optional[float] = None
archived: Optional[bool] = False
@@ -546,6 +581,7 @@ class UpdatePlanParams(BaseModel):
"price",
"items",
"free_trial",
"config",
"version",
"archived",
"new_plan_id",
@@ -1134,6 +1170,36 @@ UpdatePlanEnv = Union[
r"""Environment this plan belongs to ('sandbox' or 'live')."""
class UpdatePlanConfigResponseTypedDict(TypedDict):
r"""Miscellaneous plan-level configuration flags."""
ignore_past_due: NotRequired[bool]
r"""If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state."""
class UpdatePlanConfigResponse(BaseModel):
r"""Miscellaneous plan-level configuration flags."""
ignore_past_due: Optional[bool] = False
r"""If true, entitlements attached to this plan will still reset on schedule even when the customer's product is in a past_due state."""
@model_serializer(mode="wrap")
def serialize_model(self, handler):
optional_fields = set(["ignore_past_due"])
serialized = handler(self)
m = {}
for n, f in type(self).model_fields.items():
k = f.alias or n
val = serialized.get(k, serialized.get(n))
if val != UNSET_SENTINEL:
if val is not None or k not in optional_fields:
m[k] = val
return m
UpdatePlanStatus = Union[
Literal[
"active",
@@ -1232,6 +1298,8 @@ class UpdatePlanResponseTypedDict(TypedDict):
r"""Whether the plan is archived. Archived plans cannot be attached to new customers."""
base_variant_id: Nullable[str]
r"""If this is a variant, the ID of the base plan it was created from."""
config: UpdatePlanConfigResponseTypedDict
r"""Miscellaneous plan-level configuration flags."""
free_trial: NotRequired[UpdatePlanFreeTrialTypedDict]
r"""Free trial configuration. If set, new customers can try this plan before being charged."""
customer_eligibility: NotRequired[UpdatePlanCustomerEligibilityTypedDict]
@@ -1279,6 +1347,9 @@ class UpdatePlanResponse(BaseModel):
base_variant_id: Nullable[str]
r"""If this is a variant, the ID of the base plan it was created from."""
config: UpdatePlanConfigResponse
r"""Miscellaneous plan-level configuration flags."""
free_trial: Optional[UpdatePlanFreeTrial] = None
r"""Free trial configuration. If set, new customers can try this plan before being charged."""

View File

@@ -30,6 +30,11 @@ class Plans(BaseSDK):
free_trial: Optional[
Union[models.FreeTrialRequest, models.FreeTrialRequestTypedDict]
] = None,
config: Optional[
Union[
models.CreatePlanConfigRequest, models.CreatePlanConfigRequestTypedDict
]
] = None,
retries: OptionalNullable[utils.RetryConfig] = UNSET,
server_url: Optional[str] = None,
timeout_ms: Optional[int] = None,
@@ -50,6 +55,7 @@ class Plans(BaseSDK):
:param price: Base recurring price for the plan. Omit for free or usage-only plans.
:param items: Feature configurations for this plan. Each item defines included units, pricing, and reset behavior.
:param free_trial: Free trial configuration. Customers can try this plan before being charged.
:param config: Miscellaneous plan-level configuration flags.
:param retries: Override the default retry configuration for this method
:param server_url: Override the default server URL for this method
:param timeout_ms: Override the default request timeout configuration for this method in milliseconds
@@ -81,6 +87,9 @@ class Plans(BaseSDK):
free_trial=utils.get_pydantic_model(
free_trial, Optional[models.FreeTrialRequest]
),
config=utils.get_pydantic_model(
config, Optional[models.CreatePlanConfigRequest]
),
)
req = self._build_request(
@@ -163,6 +172,11 @@ class Plans(BaseSDK):
free_trial: Optional[
Union[models.FreeTrialRequest, models.FreeTrialRequestTypedDict]
] = None,
config: Optional[
Union[
models.CreatePlanConfigRequest, models.CreatePlanConfigRequestTypedDict
]
] = None,
retries: OptionalNullable[utils.RetryConfig] = UNSET,
server_url: Optional[str] = None,
timeout_ms: Optional[int] = None,
@@ -183,6 +197,7 @@ class Plans(BaseSDK):
:param price: Base recurring price for the plan. Omit for free or usage-only plans.
:param items: Feature configurations for this plan. Each item defines included units, pricing, and reset behavior.
:param free_trial: Free trial configuration. Customers can try this plan before being charged.
:param config: Miscellaneous plan-level configuration flags.
:param retries: Override the default retry configuration for this method
:param server_url: Override the default server URL for this method
:param timeout_ms: Override the default request timeout configuration for this method in milliseconds
@@ -214,6 +229,9 @@ class Plans(BaseSDK):
free_trial=utils.get_pydantic_model(
free_trial, Optional[models.FreeTrialRequest]
),
config=utils.get_pydantic_model(
config, Optional[models.CreatePlanConfigRequest]
),
)
req = self._build_request_async(
@@ -685,6 +703,11 @@ class Plans(BaseSDK):
models.UpdatePlanFreeTrialParamsTypedDict,
]
] = UNSET,
config: Optional[
Union[
models.UpdatePlanConfigRequest, models.UpdatePlanConfigRequestTypedDict
]
] = None,
version: Optional[float] = None,
archived: Optional[bool] = False,
new_plan_id: Optional[str] = None,
@@ -708,6 +731,7 @@ class Plans(BaseSDK):
:param price: The price of the plan. Set to null to remove the base price.
:param items: Feature configurations for this plan. Each item defines included units, pricing, and reset behavior.
:param free_trial: The free trial of the plan. Set to null to remove the free trial.
:param config: Miscellaneous plan-level configuration flags.
:param version:
:param archived:
:param new_plan_id: The new ID to use for the plan. Can only be updated if the plan has not been used by any customers.
@@ -742,6 +766,9 @@ class Plans(BaseSDK):
free_trial=utils.get_pydantic_model(
free_trial, OptionalNullable[models.UpdatePlanFreeTrialParams]
),
config=utils.get_pydantic_model(
config, Optional[models.UpdatePlanConfigRequest]
),
version=version,
archived=archived,
new_plan_id=new_plan_id,
@@ -830,6 +857,11 @@ class Plans(BaseSDK):
models.UpdatePlanFreeTrialParamsTypedDict,
]
] = UNSET,
config: Optional[
Union[
models.UpdatePlanConfigRequest, models.UpdatePlanConfigRequestTypedDict
]
] = None,
version: Optional[float] = None,
archived: Optional[bool] = False,
new_plan_id: Optional[str] = None,
@@ -853,6 +885,7 @@ class Plans(BaseSDK):
:param price: The price of the plan. Set to null to remove the base price.
:param items: Feature configurations for this plan. Each item defines included units, pricing, and reset behavior.
:param free_trial: The free trial of the plan. Set to null to remove the free trial.
:param config: Miscellaneous plan-level configuration flags.
:param version:
:param archived:
:param new_plan_id: The new ID to use for the plan. Can only be updated if the plan has not been used by any customers.
@@ -887,6 +920,9 @@ class Plans(BaseSDK):
free_trial=utils.get_pydantic_model(
free_trial, OptionalNullable[models.UpdatePlanFreeTrialParams]
),
config=utils.get_pydantic_model(
config, Optional[models.UpdatePlanConfigRequest]
),
version=version,
archived=archived,
new_plan_id=new_plan_id,

View File

@@ -307,7 +307,9 @@ class Autumn(BaseSDK):
)
if utils.match_response(http_res, "200", "application/json"):
return unmarshal_json_response(models.CheckResponse, http_res)
return unmarshal_json_response(models.CheckResponseBody1, http_res)
if utils.match_response(http_res, "202", "application/json"):
return unmarshal_json_response(models.CheckResponseBody2, http_res)
if utils.match_response(http_res, "4XX", "*"):
http_res_text = utils.stream_to_text(http_res)
raise errors.AutumnDefaultError(
@@ -420,7 +422,9 @@ class Autumn(BaseSDK):
)
if utils.match_response(http_res, "200", "application/json"):
return unmarshal_json_response(models.CheckResponse, http_res)
return unmarshal_json_response(models.CheckResponseBody1, http_res)
if utils.match_response(http_res, "202", "application/json"):
return unmarshal_json_response(models.CheckResponseBody2, http_res)
if utils.match_response(http_res, "4XX", "*"):
http_res_text = await utils.stream_to_text_async(http_res)
raise errors.AutumnDefaultError(
@@ -530,7 +534,9 @@ class Autumn(BaseSDK):
)
if utils.match_response(http_res, "200", "application/json"):
return unmarshal_json_response(models.TrackResponse, http_res)
return unmarshal_json_response(models.TrackResponseBody1, http_res)
if utils.match_response(http_res, "202", "application/json"):
return unmarshal_json_response(models.TrackResponseBody2, http_res)
if utils.match_response(http_res, "4XX", "*"):
http_res_text = utils.stream_to_text(http_res)
raise errors.AutumnDefaultError(
@@ -640,7 +646,9 @@ class Autumn(BaseSDK):
)
if utils.match_response(http_res, "200", "application/json"):
return unmarshal_json_response(models.TrackResponse, http_res)
return unmarshal_json_response(models.TrackResponseBody1, http_res)
if utils.match_response(http_res, "202", "application/json"):
return unmarshal_json_response(models.TrackResponseBody2, http_res)
if utils.match_response(http_res, "4XX", "*"):
http_res_text = await utils.stream_to_text_async(http_res)
raise errors.AutumnDefaultError(

View File

@@ -0,0 +1,242 @@
/**
* @param webhook the webhook object
* @param webhook.method destination method. Allowed values: "POST", "PUT"
* @param webhook.url current destination address
* @param webhook.eventType current webhook Event Type
* @param webhook.payload JSON payload
* @param webhook.cancel whether to cancel dispatch of the given webhook
*/
function handler(webhook) {
var AUTUMN_BASE = "https://app.useautumn.com/customers/";
var AUTUMN_USERNAME = "Autumn";
var AUTUMN_AVATAR_URL = "https://i.ibb.co/BHCF1ZqL/autumnicon.png";
// Discord embed colors (decimal RGB).
var COLOR_SUCCESS = 0x22c55e;
var COLOR_INFO = 0x3b82f6;
var COLOR_WARNING = 0xf59e0b;
var COLOR_DANGER = 0xef4444;
var COLOR_NEUTRAL = 0x6b7280;
var payload = webhook.payload || {};
var data = payload.data || payload;
// ============ customer.products.updated ============
if (webhook.eventType === "customer.products.updated") {
var scenario = data.scenario || "updated";
var customer = data.customer || {};
var entity = data.entity || null;
var product = data.updated_product || {};
var customerName = customer.name || customer.email || customer.id || "Customer";
var customerEmail = customer.email || null;
var customerId = customer.id || "";
var productName = product.name || "their plan";
if (product.version && product.version !== 1) {
productName = productName + " V" + product.version;
}
var entityLabel = null;
if (entity) {
entityLabel = entity.name || entity.id || null;
}
var emoji = "🔔";
var header = "Subscription Updated";
var verb = "updated";
var color = COLOR_NEUTRAL;
if (scenario === "new") {
emoji = "🎉"; header = "New Subscription"; verb = "subscribed to"; color = COLOR_SUCCESS;
} else if (scenario === "upgrade") {
emoji = "🚀"; header = "Customer Upgraded"; verb = "upgraded to"; color = COLOR_SUCCESS;
} else if (scenario === "downgrade") {
emoji = "📉"; header = "Customer Downgraded"; verb = "downgraded to"; color = COLOR_WARNING;
} else if (scenario === "cancel") {
emoji = "⚠️"; header = "Subscription Cancelled"; verb = "cancelled"; color = COLOR_WARNING;
} else if (scenario === "renew") {
emoji = "🔄"; header = "Subscription Uncancelled"; verb = "uncancelled"; color = COLOR_SUCCESS;
} else if (scenario === "expired") {
emoji = "💀"; header = "Subscription Expired"; verb = "expired on"; color = COLOR_DANGER;
} else if (scenario === "scheduled") {
emoji = "📅"; header = "Change Scheduled"; verb = "scheduled a change to"; color = COLOR_INFO;
}
var sentence;
if (scenario === "expired") {
sentence = "**" + customerName + "**'s **" + productName + "** expired";
} else {
sentence = "**" + customerName + "** " + verb + " **" + productName + "**";
}
var description = sentence;
if (customerId) {
description += "\n\n[View in Autumn](" + AUTUMN_BASE + customerId + ")";
}
var fields = [
{ name: "Customer", value: customerName, inline: true }
];
if (customerEmail) {
fields.push({ name: "Email", value: customerEmail, inline: true });
}
fields.push({ name: "Product", value: productName, inline: true });
fields.push({ name: "Scenario", value: "`" + scenario + "`", inline: true });
if (entityLabel) {
fields.push({ name: "Entity", value: entityLabel, inline: true });
}
var embed = {
title: emoji + " " + header,
description: description,
color: color,
fields: fields
};
if (customerId) {
embed.url = AUTUMN_BASE + customerId;
}
var footerParts = [];
if (customerId) footerParts.push("Customer ID: " + customerId);
if (entity && entity.id) footerParts.push("Entity: " + entity.id);
if (footerParts.length > 0) {
embed.footer = { text: footerParts.join(" | ") };
}
webhook.payload = {
username: AUTUMN_USERNAME,
avatar_url: AUTUMN_AVATAR_URL,
embeds: [embed]
};
return webhook;
}
// ============ balances.limit_reached ============
if (webhook.eventType === "balances.limit_reached") {
var lrCustomerId = data.customer_id || "";
var lrFeatureId = data.feature_id || "feature";
var lrLimitType = data.limit_type || "included";
var lrEntityId = data.entity_id || null;
var lrCustomerDisplay = lrCustomerId
? "[`" + lrCustomerId + "`](" + AUTUMN_BASE + lrCustomerId + ")"
: "`unknown`";
var lrDescription =
lrCustomerDisplay + " hit their **" + lrFeatureId + "** `" + lrLimitType + "` limit";
if (lrCustomerId) {
lrDescription += "\n\n[View in Autumn](" + AUTUMN_BASE + lrCustomerId + ")";
}
var lrFields = [
{
name: "Customer",
value: lrCustomerId ? "`" + lrCustomerId + "`" : "—",
inline: true
},
{ name: "Feature", value: "`" + lrFeatureId + "`", inline: true },
{ name: "Limit Type", value: "`" + lrLimitType + "`", inline: true }
];
if (lrEntityId) {
lrFields.push({ name: "Entity", value: "`" + lrEntityId + "`", inline: true });
}
var lrEmbed = {
title: "🚫 Limit Reached",
description: lrDescription,
color: COLOR_DANGER,
fields: lrFields
};
if (lrCustomerId) {
lrEmbed.url = AUTUMN_BASE + lrCustomerId;
}
var lrFooterParts = [];
if (lrCustomerId) lrFooterParts.push("Customer ID: " + lrCustomerId);
if (lrEntityId) lrFooterParts.push("Entity: " + lrEntityId);
if (lrFooterParts.length > 0) {
lrEmbed.footer = { text: lrFooterParts.join(" | ") };
}
webhook.payload = {
username: AUTUMN_USERNAME,
avatar_url: AUTUMN_AVATAR_URL,
embeds: [lrEmbed]
};
return webhook;
}
// ============ balances.usage_alert_triggered ============
if (webhook.eventType === "balances.usage_alert_triggered") {
var uaCustomerId = data.customer_id || "";
var uaFeatureId = data.feature_id || "feature";
var uaEntityId = data.entity_id || null;
var uaAlert = data.usage_alert || {};
var uaAlertName = uaAlert.name || "Usage alert";
var uaThreshold = uaAlert.threshold;
var uaThresholdType = uaAlert.threshold_type || "usage";
var uaThresholdLabel = "—";
if (uaThreshold !== undefined && uaThreshold !== null) {
if (uaThresholdType === "usage_percentage") {
uaThresholdLabel = uaThreshold + "% used";
} else if (uaThresholdType === "remaining_percentage") {
uaThresholdLabel = uaThreshold + "% remaining";
} else if (uaThresholdType === "remaining") {
uaThresholdLabel = uaThreshold + " remaining";
} else {
uaThresholdLabel = uaThreshold + " used";
}
}
var uaCustomerDisplay = uaCustomerId
? "[`" + uaCustomerId + "`](" + AUTUMN_BASE + uaCustomerId + ")"
: "`unknown`";
var uaDescription =
uaCustomerDisplay + " crossed the **" + uaAlertName + "** threshold on **" + uaFeatureId + "**";
if (uaCustomerId) {
uaDescription += "\n\n[View in Autumn](" + AUTUMN_BASE + uaCustomerId + ")";
}
var uaFields = [
{
name: "Customer",
value: uaCustomerId ? "`" + uaCustomerId + "`" : "—",
inline: true
},
{ name: "Feature", value: "`" + uaFeatureId + "`", inline: true },
{ name: "Alert", value: uaAlertName, inline: true },
{ name: "Threshold", value: uaThresholdLabel, inline: true }
];
if (uaEntityId) {
uaFields.push({ name: "Entity", value: "`" + uaEntityId + "`", inline: true });
}
var uaEmbed = {
title: "📊 Usage Alert: " + uaAlertName,
description: uaDescription,
color: COLOR_WARNING,
fields: uaFields
};
if (uaCustomerId) {
uaEmbed.url = AUTUMN_BASE + uaCustomerId;
}
var uaFooterParts = [];
if (uaCustomerId) uaFooterParts.push("Customer ID: " + uaCustomerId);
if (uaEntityId) uaFooterParts.push("Entity: " + uaEntityId);
if (uaFooterParts.length > 0) {
uaEmbed.footer = { text: uaFooterParts.join(" | ") };
}
webhook.payload = {
username: AUTUMN_USERNAME,
avatar_url: AUTUMN_AVATAR_URL,
embeds: [uaEmbed]
};
return webhook;
}
// Unmatched event type — cancel dispatch.
webhook.cancel = true;
return webhook;
}

View File

@@ -0,0 +1,292 @@
/**
* @param webhook the webhook object
* @param webhook.method destination method. Allowed values: "POST", "PUT"
* @param webhook.url current destination address
* @param webhook.eventType current webhook Event Type
* @param webhook.payload JSON payload
* @param webhook.cancel whether to cancel dispatch of the given webhook
*/
function handler(webhook) {
var AUTUMN_BASE = "https://app.useautumn.com/customers/";
var AUTUMN_USERNAME = "Autumn";
var AUTUMN_ICON_URL = "https://i.ibb.co/BHCF1ZqL/autumnicon.png";
var payload = webhook.payload || {};
var data = payload.data || payload;
// ============ customer.products.updated ============
if (webhook.eventType === "customer.products.updated") {
var scenario = data.scenario || "updated";
var customer = data.customer || {};
var entity = data.entity || null;
var product = data.updated_product || {};
var customerName = customer.name || customer.email || customer.id || "Customer";
var customerEmail = customer.email || null;
var customerId = customer.id || "";
var productName = product.name || "their plan";
if (product.version && product.version !== 1) {
productName = productName + " V" + product.version;
}
var entityLabel = null;
if (entity) {
entityLabel = entity.name || entity.id || null;
}
var meta = {
"new": { emoji: "🎉", header: "New Subscription", verb: "subscribed to" },
"upgrade": { emoji: "🚀", header: "Customer Upgraded", verb: "upgraded to" },
"downgrade": { emoji: "📉", header: "Customer Downgraded", verb: "downgraded to" },
"cancel": { emoji: "⚠️", header: "Subscription Cancelled", verb: "cancelled" },
"renew": { emoji: "🔄", header: "Subscription Uncancelled", verb: "uncancelled" },
"expired": { emoji: "💀", header: "Subscription Expired", verb: "expired on" },
"scheduled": { emoji: "📅", header: "Change Scheduled", verb: "scheduled a change to" }
}[scenario] || { emoji: "🔔", header: "Subscription Updated", verb: "updated" };
var sentence;
if (scenario === "expired") {
sentence = "*" + customerName + "*'s *" + productName + "* expired";
} else {
sentence = "*" + customerName + "* " + meta.verb + " *" + productName + "*";
}
var previewText = meta.emoji + " " + customerName + " " + meta.verb + " " + productName;
var fields = [
{ type: "mrkdwn", text: "*Customer:*\n" + customerName }
];
if (customerEmail) {
fields.push({ type: "mrkdwn", text: "*Email:*\n" + customerEmail });
}
fields.push({ type: "mrkdwn", text: "*Product:*\n" + productName });
fields.push({ type: "mrkdwn", text: "*Scenario:*\n`" + scenario + "`" });
if (entityLabel) {
fields.push({ type: "mrkdwn", text: "*Entity:*\n" + entityLabel });
}
var contextParts = [];
if (customerId) {
contextParts.push("Customer ID: `" + customerId + "`");
}
if (entity && entity.id) {
contextParts.push("Entity: `" + entity.id + "`");
}
var blocks = [
{
type: "header",
text: { type: "plain_text", text: meta.emoji + " " + meta.header, emoji: true }
},
{
type: "section",
text: { type: "mrkdwn", text: sentence }
},
{
type: "section",
fields: fields
}
];
if (customerId) {
blocks.push({
type: "actions",
elements: [
{
type: "button",
text: { type: "plain_text", text: "View in Autumn", emoji: true },
url: AUTUMN_BASE + customerId,
style: "primary"
}
]
});
}
if (contextParts.length > 0) {
blocks.push({
type: "context",
elements: [
{ type: "mrkdwn", text: contextParts.join(" | ") }
]
});
}
webhook.payload = {
username: AUTUMN_USERNAME,
icon_url: AUTUMN_ICON_URL,
text: previewText,
blocks: blocks
};
return webhook;
}
// ============ balances.limit_reached ============
if (webhook.eventType === "balances.limit_reached") {
var lrCustomerId = data.customer_id || "";
var lrFeatureId = data.feature_id || "feature";
var lrLimitType = data.limit_type || "included";
var lrEntityId = data.entity_id || null;
var lrCustomerLink = lrCustomerId
? "<" + AUTUMN_BASE + lrCustomerId + "|`" + lrCustomerId + "`>"
: "`unknown`";
var lrSentence =
lrCustomerLink + " hit their *" + lrFeatureId + "* `" + lrLimitType + "` limit";
var lrPreview = "🚫 " + lrCustomerId + " hit their " + lrFeatureId + " limit";
var lrFields = [
{ type: "mrkdwn", text: "*Customer:*\n" + lrCustomerLink },
{ type: "mrkdwn", text: "*Feature:*\n`" + lrFeatureId + "`" },
{ type: "mrkdwn", text: "*Limit Type:*\n`" + lrLimitType + "`" }
];
if (lrEntityId) {
lrFields.push({ type: "mrkdwn", text: "*Entity:*\n`" + lrEntityId + "`" });
}
var lrContextParts = [];
if (lrCustomerId) lrContextParts.push("Customer ID: `" + lrCustomerId + "`");
if (lrEntityId) lrContextParts.push("Entity: `" + lrEntityId + "`");
var lrBlocks = [
{
type: "header",
text: { type: "plain_text", text: "🚫 Limit Reached", emoji: true }
},
{
type: "section",
text: { type: "mrkdwn", text: lrSentence }
},
{
type: "section",
fields: lrFields
}
];
if (lrCustomerId) {
lrBlocks.push({
type: "actions",
elements: [
{
type: "button",
text: { type: "plain_text", text: "View in Autumn", emoji: true },
url: AUTUMN_BASE + lrCustomerId,
style: "danger"
}
]
});
}
if (lrContextParts.length > 0) {
lrBlocks.push({
type: "context",
elements: [
{ type: "mrkdwn", text: lrContextParts.join(" | ") }
]
});
}
webhook.payload = {
username: AUTUMN_USERNAME,
icon_url: AUTUMN_ICON_URL,
text: lrPreview,
blocks: lrBlocks
};
return webhook;
}
// ============ balances.usage_alert_triggered ============
if (webhook.eventType === "balances.usage_alert_triggered") {
var uaCustomerId = data.customer_id || "";
var uaFeatureId = data.feature_id || "feature";
var uaEntityId = data.entity_id || null;
var uaAlert = data.usage_alert || {};
var uaAlertName = uaAlert.name || "Usage alert";
var uaThreshold = uaAlert.threshold;
var uaThresholdType = uaAlert.threshold_type || "usage";
function formatThreshold(value, type) {
if (value === undefined || value === null) return "—";
if (type === "usage_percentage") return value + "% used";
if (type === "remaining_percentage") return value + "% remaining";
if (type === "remaining") return value + " remaining";
return value + " used";
}
var uaThresholdLabel = formatThreshold(uaThreshold, uaThresholdType);
var uaCustomerLink = uaCustomerId
? "<" + AUTUMN_BASE + uaCustomerId + "|`" + uaCustomerId + "`>"
: "`unknown`";
var uaSentence =
uaCustomerLink + " crossed the *" + uaAlertName + "* threshold on *" + uaFeatureId + "*";
var uaPreview = "📊 Usage Alert: " + uaAlertName + " (" + uaCustomerId + ")";
var uaFields = [
{ type: "mrkdwn", text: "*Customer:*\n" + uaCustomerLink },
{ type: "mrkdwn", text: "*Feature:*\n`" + uaFeatureId + "`" },
{ type: "mrkdwn", text: "*Alert:*\n" + uaAlertName },
{ type: "mrkdwn", text: "*Threshold:*\n" + uaThresholdLabel }
];
if (uaEntityId) {
uaFields.push({ type: "mrkdwn", text: "*Entity:*\n`" + uaEntityId + "`" });
}
var uaContextParts = [];
if (uaCustomerId) uaContextParts.push("Customer ID: `" + uaCustomerId + "`");
if (uaEntityId) uaContextParts.push("Entity: `" + uaEntityId + "`");
var uaBlocks = [
{
type: "header",
text: { type: "plain_text", text: "📊 Usage Alert: " + uaAlertName, emoji: true }
},
{
type: "section",
text: { type: "mrkdwn", text: uaSentence }
},
{
type: "section",
fields: uaFields
}
];
if (uaCustomerId) {
uaBlocks.push({
type: "actions",
elements: [
{
type: "button",
text: { type: "plain_text", text: "View in Autumn", emoji: true },
url: AUTUMN_BASE + uaCustomerId
}
]
});
}
if (uaContextParts.length > 0) {
uaBlocks.push({
type: "context",
elements: [
{ type: "mrkdwn", text: uaContextParts.join(" | ") }
]
});
}
webhook.payload = {
username: AUTUMN_USERNAME,
icon_url: AUTUMN_ICON_URL,
text: uaPreview,
blocks: uaBlocks
};
return webhook;
}
// Cancel any other event types — they don't match Slack's expected schema and would error.
webhook.cancel = true;
return webhook;
}

View File

@@ -1,6 +1,7 @@
{
"name": "autumn",
"private": true,
"packageManager": "bun@1.3.10",
"workspaces": {
"packages": [
"server",
@@ -83,6 +84,7 @@
"setup": "node scripts/setup/setup.js",
"setup:s3-admin": "bun scripts/setup/setupS3Admin.ts",
"setup:test": "infisical run --env=dev --recursive -- bun scripts/setup/setup-test.ts",
"stripe:link-test": "ENV_FILE=.env infisical run --env=dev --recursive -- bun scripts/setup/link-test-stripe-account.ts",
"agent:bootstrap": "bash scripts/setup/agent-bootstrap.sh",
"dev:agent": "bash scripts/setup/devAgent.sh",
"migrate-functions": "infisical run --env=dev --recursive -- bun scripts/migrations/migrate-functions.ts",
@@ -110,7 +112,7 @@
"site": "cd apps/website && bun dev && cd ../..",
"docs": "bun -F @autumn/docs dev",
"docs:build": "bun -F @autumn/docs build",
"ts": "bun -F @autumn/server ts && bun -F autumn-js ts && bun -F @autumn/openapi ts && bun -F atmn ts && bun -F checkout ts",
"ts": "turbo run ts --filter=@autumn/server --filter=autumn-js --filter=@autumn/openapi --filter=atmn --filter=checkout",
"atmn:build": "bun -F atmn build",
"openapi:ts": "bun -F @autumn/openapi ts",
"js:ts": "bun -F autumn-js ts",
@@ -143,6 +145,7 @@
"husky": "^9.1.7",
"inquirer": "^12.10.0",
"knip": "^6.7.0",
"ts-to-zod": "^5.1.0"
"ts-to-zod": "^5.1.0",
"turbo": "^2.9.6"
}
}

View File

@@ -1,7 +1,7 @@
{
"name": "autumn-js",
"description": "Autumn JS Library",
"version": "1.2.10",
"version": "1.2.17",
"repository": "github:useautumn/autumn",
"homepage": "https://docs.useautumn.com",
"main": "./dist/sdk/index.js",

View File

@@ -10,6 +10,8 @@ export type HandleBetterAuthRouteFn = (args: {
routeName: RouteName;
}) => Promise<{ status: number; body: unknown }>;
export type AutumnEndpoint = ReturnType<typeof createAuthEndpoint>;
/** Get route config by name from routeConfigs */
const getRouteConfig = (routeName: RouteName) => {
const route = routeConfigs.find((r) => r.route === routeName);
@@ -24,7 +26,7 @@ const getRouteConfig = (routeName: RouteName) => {
export const createAutumnEndpoint = <T extends RouteName>(
routeName: T,
handleRoute: HandleBetterAuthRouteFn,
) => {
): AutumnEndpoint => {
const config = getRouteConfig(routeName);
return createAuthEndpoint(
`/autumn/${routeName}` as `/autumn/${T}`,
@@ -38,5 +40,5 @@ export const createAutumnEndpoint = <T extends RouteName>(
status: result.status,
});
},
);
) as AutumnEndpoint;
};

View File

@@ -196,6 +196,7 @@ export const attachParamsOutboundSchema = z.object({
.union([z.record(z.string(), z.string()), z.undefined()])
.optional(),
no_billing_changes: z.union([z.boolean(), z.undefined()]).optional(),
enable_plan_immediately: z.union([z.boolean(), z.undefined()]).optional(),
});
const closedEnumSchema = z.any();
@@ -324,6 +325,7 @@ export const attachParamsSchema = z.object({
.union([z.record(z.string(), z.string()), z.undefined()])
.optional(),
noBillingChanges: z.union([z.boolean(), z.undefined()]).optional(),
enablePlanImmediately: z.union([z.boolean(), z.undefined()]).optional(),
});
export const attachCodeSchema = openEnumSchema;

View File

@@ -31,6 +31,10 @@ export const listPlansItemDisplaySchema = z.object({
secondaryText: z.union([z.string(), z.undefined()]).optional(),
});
export const listPlansConfigSchema = z.object({
ignorePastDue: z.boolean(),
});
export const listPlansParamsOutboundSchema = z.object({
customer_id: z.union([z.string(), z.undefined()]).optional(),
entity_id: z.union([z.string(), z.undefined()]).optional(),
@@ -148,6 +152,7 @@ export const listPlansListSchema = z.object({
env: listPlansEnvSchema,
archived: z.boolean(),
baseVariantId: z.string().nullable(),
config: listPlansConfigSchema,
customerEligibility: z
.union([listPlansCustomerEligibilitySchema, z.undefined()])
.optional(),

View File

@@ -345,6 +345,7 @@ export const multiAttachParamsSchema = z.object({
.union([multiAttachRedirectModeSchema, z.undefined()])
.optional(),
newBillingSubscription: z.union([z.boolean(), z.undefined()]).optional(),
enablePlanImmediately: z.union([z.boolean(), z.undefined()]).optional(),
customerData: z.union([customerDataSchema, z.undefined()]).optional(),
entityData: z.union([multiAttachEntityDataSchema, z.undefined()]).optional(),
});
@@ -386,6 +387,7 @@ export const multiAttachParamsOutboundSchema = z.object({
.optional(),
redirect_mode: z.string(),
new_billing_subscription: z.union([z.boolean(), z.undefined()]).optional(),
enable_plan_immediately: z.union([z.boolean(), z.undefined()]).optional(),
customer_data: z
.union([customerDataOutboundSchema, z.undefined()])
.optional(),

View File

@@ -287,6 +287,7 @@ export const previewAttachParamsOutboundSchema = z.object({
.union([z.record(z.string(), z.string()), z.undefined()])
.optional(),
no_billing_changes: z.union([z.boolean(), z.undefined()]).optional(),
enable_plan_immediately: z.union([z.boolean(), z.undefined()]).optional(),
});
const closedEnumSchema = z.any();
@@ -432,6 +433,7 @@ export const previewAttachParamsSchema = z.object({
.union([z.record(z.string(), z.string()), z.undefined()])
.optional(),
noBillingChanges: z.union([z.boolean(), z.undefined()]).optional(),
enablePlanImmediately: z.union([z.boolean(), z.undefined()]).optional(),
});
export const previewAttachIncomingSchema = z.object({
@@ -454,6 +456,16 @@ export const previewAttachOutgoingSchema = z.object({
export const previewAttachCheckoutTypeSchema = openEnumSchema;
export const previewAttachStatusSchema = openEnumSchema;
export const previewAttachTaxSchema = z.object({
total: z.number(),
amountInclusive: z.number(),
amountExclusive: z.number(),
currency: z.string(),
status: previewAttachStatusSchema,
});
export const previewAttachResponseSchema = z.object({
customerId: z.string(),
lineItems: z.array(previewAttachLineItemSchema),
@@ -466,4 +478,5 @@ export const previewAttachResponseSchema = z.object({
outgoing: z.array(previewAttachOutgoingSchema),
redirectToCheckout: z.boolean(),
checkoutType: previewAttachCheckoutTypeSchema.nullable(),
tax: z.union([previewAttachTaxSchema, z.undefined()]).optional(),
});

View File

@@ -451,6 +451,7 @@ export const previewMultiAttachParamsSchema = z.object({
.union([previewMultiAttachRedirectModeSchema, z.undefined()])
.optional(),
newBillingSubscription: z.union([z.boolean(), z.undefined()]).optional(),
enablePlanImmediately: z.union([z.boolean(), z.undefined()]).optional(),
customerData: z.union([customerDataSchema, z.undefined()]).optional(),
entityData: z
.union([previewMultiAttachEntityDataSchema, z.undefined()])
@@ -477,6 +478,16 @@ export const previewMultiAttachOutgoingSchema = z.object({
export const previewMultiAttachCheckoutTypeSchema = openEnumSchema;
export const previewMultiAttachStatusSchema = openEnumSchema;
export const previewMultiAttachTaxSchema = z.object({
total: z.number(),
amountInclusive: z.number(),
amountExclusive: z.number(),
currency: z.string(),
status: previewMultiAttachStatusSchema,
});
export const previewMultiAttachResponseSchema = z.object({
customerId: z.string(),
lineItems: z.array(previewMultiAttachLineItemSchema),
@@ -491,6 +502,7 @@ export const previewMultiAttachResponseSchema = z.object({
outgoing: z.array(previewMultiAttachOutgoingSchema),
redirectToCheckout: z.boolean(),
checkoutType: previewMultiAttachCheckoutTypeSchema.nullable(),
tax: z.union([previewMultiAttachTaxSchema, z.undefined()]).optional(),
});
export const previewMultiAttachParamsOutboundSchema = z.object({
@@ -516,6 +528,7 @@ export const previewMultiAttachParamsOutboundSchema = z.object({
.optional(),
redirect_mode: z.string(),
new_billing_subscription: z.union([z.boolean(), z.undefined()]).optional(),
enable_plan_immediately: z.union([z.boolean(), z.undefined()]).optional(),
customer_data: z
.union([customerDataOutboundSchema, z.undefined()])
.optional(),

View File

@@ -184,6 +184,7 @@ export const setupPaymentParamsOutboundSchema = z.object({
.union([z.record(z.string(), z.string()), z.undefined()])
.optional(),
no_billing_changes: z.union([z.boolean(), z.undefined()]).optional(),
enable_plan_immediately: z.union([z.boolean(), z.undefined()]).optional(),
});
const closedEnumSchema = z.any();
@@ -311,4 +312,5 @@ export const setupPaymentParamsSchema = z.object({
.union([z.record(z.string(), z.string()), z.undefined()])
.optional(),
noBillingChanges: z.union([z.boolean(), z.undefined()]).optional(),
enablePlanImmediately: z.union([z.boolean(), z.undefined()]).optional(),
});

Some files were not shown because too many files have changed in this diff Show More