Skip to content

Commit 260b02b

Browse files
committed
feat(api): upgrade autumn-js to v1.2.2 and rewrite webhook handler
- Import path: autumn-js/elysia → autumn-js/fetch, .use() → .mount() - Webhook events: customer.threshold_reached → balances.limit_reached, add balances.usage_alert_triggered with new UsageAlertEmail template - Slack notifications now cover all plan scenarios (cancel, downgrade, expired, past_due) with per-scenario priority - Deduplicated email flow into sendAlertEmailAction with cooldown/log - Handler dispatch via map for easy extension
1 parent 3f1fe16 commit 260b02b

7 files changed

Lines changed: 386 additions & 318 deletions

File tree

.agents/skills/databuddy/SKILL.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,17 @@ Read [codebase-map.md](./references/codebase-map.md) when you need deeper routin
7777

7878
- Start in `apps/basket/src`
7979
- Request validation, billing checks, geo/IP parsing, producer logic, and structured errors are important here
80+
81+
## Billing (Autumn)
82+
83+
- `autumn-js` v1.2.2+ — import `autumnHandler` from `autumn-js/fetch` (NOT `autumn-js/elysia`, that export was removed in v1.0)
84+
- For Elysia, mount with `.mount(autumnHandler(...))` — NOT `.use()`
85+
- `identify` callback receives `(request: Request)` directly, not `({ request })`
86+
- Webhook event types: `balances.limit_reached` (replaces old `customer.threshold_reached`), `customer.products.updated`, `balances.usage_alert_triggered`
87+
- `balances.limit_reached` payload is flat: `{ customer_id, feature_id, entity_id?, limit_type }` — no full customer object
88+
- SDK `Customer` type uses camelCase (`balances`, `subscriptions`, `overageAllowed`), but **webhook payloads are snake_case** and use old field names (`features`, `products`, `included_usage`, `overage_allowed`) — do NOT use the SDK `Customer` type for webhooks
89+
- SDK class is `new Autumn()` (reads `AUTUMN_SECRET_KEY` from env); methods use camelCase: `customerId`, `featureId`, `sendEvent`
90+
- `autumn-js` catalog version is in root `package.json` — update it when bumping
8091
- Storage and schema concerns usually continue into `packages/db`
8192
- **evlog → Axiom:** never use top-level `error` as a **string** on `log.error({ ... })` (e.g. process handlers); it overwrites structured `error.message` on the wide event. Use `error_message` instead. Basket/API drains run `normalizeWideEventForAxiom` before ingest; 4xx `EvlogError` rows are emitted as `level: "warn"` with `client_http_error: true` so Axiom “errors” are not inflated by expected client failures.
8293

apps/api/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
"@orpc/server": "^1.13.4",
4343
"@orpc/zod": "^1.13.4",
4444
"ai": "^6.0.78",
45-
"autumn-js": "^0.1.69",
45+
"autumn-js": "^1.2.2",
4646
"bullmq": "^5.66.5",
4747
"dayjs": "^1.11.19",
4848
"elysia": "^1.4.22",

apps/api/src/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { OpenAPIReferencePlugin } from "@orpc/openapi/plugins";
1313
import { ORPCError, onError } from "@orpc/server";
1414
import { RPCHandler } from "@orpc/server/fetch";
1515
import { ZodToJsonSchemaConverter } from "@orpc/zod/zod4";
16-
import { autumnHandler } from "autumn-js/elysia";
16+
import { autumnHandler } from "autumn-js/fetch";
1717
import { Elysia } from "elysia";
1818
import { initLogger, log, parseError } from "evlog";
1919
import { evlog, useLogger } from "evlog/elysia";
@@ -278,9 +278,9 @@ const app = new Elysia({ precompile: true })
278278
})
279279
)
280280
.use(webhooks)
281-
.use(
281+
.mount(
282282
autumnHandler({
283-
identify: async ({ request }) => {
283+
identify: async (request) => {
284284
try {
285285
const session = await auth.api.getSession({
286286
headers: request.headers,

0 commit comments

Comments
 (0)