Skip to main content

Public log

Changelog

A complete record of what we shipped, in reverse chronological order. We version by CalVer (year.sprint-group) until v1.

Changelog

All notable changes to AgenticVexa. Dates are in YYYY-MM-DD. Versions follow

CalVerYEAR.SPRINT_GROUP until we cut a v1.

[2026.5-gateway-ops] – 2026-05-17

Operational layer on top of the gateway shipped in [2026.5-gateway]:

auth options, health/RPM controls, async-job attribution, customer

visibility into available models. The 100-call burst against ohgrtapi

that previously produced 40 503s now succeeds 100% — 84 stayed on

the LAN GPU box (kokoro), 16 fell over to OpenAI's tts-1 cleanly.

Added

  • Google Sign-In (POST /v1/auth/google). Browser obtains an ID

token via Google Identity Services; backend verifies it against

Google's JWKS, accepts the audience for either web or iOS client,

upserts a User with provider="google". New email accounts are

pre-verified (Google's own attestation). Existing email+password

accounts get their Google identity *linked*, password preserved —

either method works after. Wired into the developer portal

(/login, /register) and admin console (/login, with the

same is_superadmin gate the password path has). No client

secret required.

  • Provider health probe, every 5 minutes. New columns

`ai_providers.last_health_status / last_health_latency_ms /

last_health_check_at`. Periodic Celery task pings each active

provider's base_url and fires admin.provider.{down,recovered}

on real status transitions — never on repeats, so a chronically

dead provider doesn't page the team every 5 min.

  • On-demand probe button at POST /v1/admin/providers/{id}/probe

+ UI button on each row. Same alert semantics as the periodic

sweep so the rule "only real transitions page" still holds.

  • Provider-wide RPM cap. New ai_providers.rate_limit_rpm

(integer, NULL = no cap). ai_service._dispatch calls

acquire_for_provider(slug, rpm) before the adapter call.

UpstreamRateLimitExceeded is reframed as a retriable

ProviderError(429) so the chain falls over to the next route.

ohgrtapi is seeded at 50 rpm — 10 under its 60/IP gateway cap.

  • Async-job provider attribution. New columns

ai_jobs.provider_slug / provider_cost_inr. Worker lifts

_provider_meta from the result dict (instead of just stripping

it) so async traffic shows up in /admin/providers/margin

instead of bucketing as unattributed.

  • Customer-visible model picker in the developer portal /docs

page — renders GET /v1/ai/models as a live table.

  • avx CLI --model flag on text/image/voice/stt/vision +

avx models subcommand for the live catalog.

  • Admin weekly digest (admin.digest.weekly) Monday 09:30 IST.

Rolls up 7d signups, new subs / MRR added, cancellations / MRR

lost, payment failures, revenue captured, top orgs by credits,

provider margin per-provider. Reuses notify_superadmins to

fan out.

  • admin.subscription.downgraded template + dispatch in

change_user_plan — symmetry with the upgrade path so churn is

visible in real time.

  • Audit logs for provider/route mutations. Every create/

update/delete on ai_providers and ai_provider_routes writes

an audit_log row with before/after diffs.

Changed

  • OhGrtAPI base_url switched to LAN IP http://192.168.1.15:8100

while public DNS for agenticvexa.in is unreliable. Verified live —

text 3.34s, image 9.95s, voice 4.67s.

Verified end-to-end

  • 100×voice burst (no model pin): 100/100 succeeded · 84 ohgrtapi +

16 openai · zero 503s · ₹7.40 total cost. Same shape as the

100×image batch which had been 100/100 since the route-table

dispatch landed.

  • Google sign-in: page renders the GIS button (340×44) on

developer/login, developer/register, admin/login; /v1/auth/google

upserts a user, bootstraps the personal org, returns access +

refresh tokens; Playwright probe of the post-token half lands at

/dashboard with the authenticated chrome.

  • Provider health: ohgrtapi 22-84 ms (LAN), OpenAI ~1100 ms (WAN),

transitions fire admin.provider.{down,recovered} exactly once

per change.

[2026.5-gateway] – 2026-05-16

Multi-provider AI gateway. Five-phase build that swapped the previously

hardcoded env-var dispatch for a route-table driven failover chain, added

cost-aware routing, and exposed model selection to customers.

Added

  • ai_providers + ai_provider_routes tables (migration 094faa68162b).

Catalog is admin-managed via /admin/providers; routes ordered by

priority ASC drive the runtime failover chain.

  • OhGrtAPI provider (operator's own GPU host — SD 1.5 / Kokoro TTS /

LangGraph + Llama 3.2 3B) seeded as priority-0 for text/image/voice.

OpenAI sits at priority-1 as failover, primary for stt/vision.

  • provider_router.resolve(db, service) + _dispatch() in ai_service.py.

Walks the chain, falls over on ProviderError(retriable=True), surfaces

non-retriable immediately (auth/quota/bad-body).

  • Per-provider adapters under services/provider_adapters/

openai_adapter.py (all 5 modalities) and ohgrtapi_adapter.py

(text/image/voice; stt+vision raise retriable so failover hits OpenAI).

  • Cost-aware routing (migration c1d49bea7e21):

ai_provider_routes.cost_per_call_inr editable in the admin UI;

api_logs.provider_slug + api_logs.provider_cost_inr stamped at

request time so margin reporting doesn't move under retroactive price

edits.

  • GET /v1/admin/providers/margin?days=N + new "Margin" tab in

/admin/providers (24h / 7d / 30d / 90d) with per-(provider, service)

rollups of call count, revenue (credits), and cost (₹).

  • Customer-visible model picker: every AI request body now accepts

model: str | None. Unknown model → 400 with a pointer to

/v1/ai/models. Default ("no preference") preserves the full chain.

  • Public GET /v1/ai/models endpoint. Developer portal /docs

renders the live catalog so customers can see what they can pin.

  • TS SDK (packages/sdk) — added model? to ImageRequest /

VoiceRequest / TranscribeRequest / VisionRequest, and a

client.models.list() accessor returning AvailableModel[].

  • Python SDK (packages/sdk-python) — client.models.list() and

matching async variant. model flows through **kwargs already.

  • Pytest coverage (tests/test_provider_gateway.py) — 12 tests

covering resolve ordering/filters and _dispatch failover, model

filter, and provider_meta stamping.

Changed

  • `ai_service.text_generate / image_generate / voice_synthesize /

transcribe / vision_analyze all accept an optional model` kwarg that

flows into _dispatch(..., model=).

  • routers/ai.py._log_request accepts provider_meta and writes

provider_slug + provider_cost_inr onto the api_logs row.

  • _classify_provider_error now handles ProviderError: 404 → 400

(customer fixes body), 401/403 → 503 (operator auth issue), other →

503 with message.

Notes

  • Existing deployments don't need a re-seed — an empty catalog falls

back to the legacy env-var path (OPENAI_API_KEY etc.) unchanged.

Re-seed to pick up the OhGrtAPI provider and the cost defaults.

[2026.5-mobile7] – 2026-05-16

Phase 6 mobile audit — closed the audit. WCAG AA-clean across all 42 routes × 2 viewports. New contrast + landmark + close-target probes; sweeping content + token fixes; mobile-first 404 / error pages on every app.

Added

  • closeTargets probe (rule I2): flags pairs of standalone tap targets <8px apart. Skips role="tablist" | "group" | "radiogroup" (segmented controls).
  • lowContrast probe (rule C1/C2): WCAG ratio with alpha compositing of ancestor backgrounds so semi-transparent layers (bg-brand-primary/10) resolve to what's actually visible on screen. Skips aria-hidden, role="presentation", role="none". Honors WCAG large-text relaxation (3:1) for ≥18px regular or ≥14px bold.
  • missingLandmarks + multipleH1s probes (rule L1): every page must expose <main>, <header>, <nav>, <footer> (or matching ARIA roles), and exactly one <h1>.
  • SecondaryChrome for landing (apps/landing/src/components/secondary-chrome.tsx): shared header + nav + footer for blog, changelog, legal pages, 404, and error pages.
  • AuthShell for developer and admin (apps/{developer,admin}/src/components/auth-shell.tsx): shared header + nav + main + footer for pre-login pages.
  • Mobile-first 404 page per app (not-found.tsx) — wrapped in its shell, full landmarks, min-h-[44px] CTAs.
  • Error boundary page per app (error.tsx) — catches uncaught render errors, shows mobile-first recovery screen with reset() + "Go home" actions, surfaces Next.js digest ref.

Fixed

  • Contrast token bump: --color-text-tertiary #71717A#8B8B95 across all 4 apps' globals.css. Was 4.12:1, now 5.0:1. Lifts dozens of subdued-text instances above WCAG AA in one change.
  • Shared Button primary variant (packages/ui/src/button.tsx): bg-[var(--color-brand-primary)] (#6366F1, 4.47:1) → bg-[#4F46E5] indigo-600 (5.6:1). Hover/active still flips to brand-secondary violet.
  • Admin Sign In button: bg-error (#EF4444, 3.76:1) → bg-[#B91C1C] red-700 (6.1:1).
  • Hero "1 credit" usage chip: colored text on tinted bg → white text on stronger-tint bg; remains color-coded but always legible.
  • Blog page contrast/tap targets: "All" filter text-text-brand on bg-brand-primary/15, category chips → text-text-secondary, "← Back to home" → text-text-secondary. Featured + post-card category chips bumped. "All" pill expanded to 44×44.
  • Developer code-language tabs (developer-section.tsx): active tab text-brand-primarytext-text-brand; added role="tablist" + role="tab" + aria-selected.
  • Playground credit chips (service-page + landing): text-text-tertiarytext-text-secondary.
  • Adjacent tap-target spacing: product-tour + testimonials carousel dots gap-1gap-2; pricing Monthly/Yearly toggle wrapped in role="group" aria-label="Billing period"; playground image example chips gap-1.5gap-2 + role="group".
  • Decorative · separators in heroaria-hidden="true" so SRs skip and contrast probes don't flag.
  • F1 a11y on remaining auth pages: forgot-password got inline role="alert" aria-live="polite" banner, aria-invalid, aria-describedby, inputMode="email", mobile-friendly autocapitalize/autocorrect/spellcheck attrs, noValidate. Verify-email error state wrapped in role="alert" aria-live="polite". Reset-password verified already F1-compliant.
  • Landmark structure across non-home pages: changelog, legal/terms, legal/privacy, legal/cookies, blog, blog/[slug] all wrapped in <SecondaryChrome>. Developer login/register/forgot/reset/verify/resend-verification all wrapped in <AuthShell>. Admin login/forgot-password/reset-password all wrapped in <AuthShell>. Playground header inner div upgraded to <nav>, new <footer> added.
  • Changelog markdown heading levels demoted by 1 so the page renders a single <h1> (the page title), keeping the document outline sensible for SR rotor navigation.

Verification (iPhone 13 + Pixel 7)

| App | Routes | hScroll | inputsNoLabel | smallTargets | closeTargets | lowContrast | landmarks | h1s |

|---|---|---|---|---|---|---|---|---|

| landing | 6 (+ 404) | clean | 0 | 0–1* | 0 | 0 | all present | 1 |

| playground | 7 (+ 404) | clean | 0 | 0 | 0 | 0 | all present | 1 |

| developer | 12 (+ 404) | clean | 0 | 0 | 0 | 0 | all present | 1 |

| admin | 14 (+ 404) | clean | 0 | 0 | 0 | 0 | all present | 1 |

*Lone remainder is the chatbot bubble caught mid motion.div enter animation; measurement artifact, not a real bug.

Total: 42 routes × 2 viewports = 84 audits, all passing the rulebook end-to-end.

[2026.5-mobile6] – 2026-05-16

Phase 5 mobile audit — finished the marketing-CTA sweep, image-alt audit, universal focus rings, register-form a11y, and probe refinements.

Fixed

  • All remaining landing-home small touch targets bumped to ≥44×44: product-tour tabs + dots, testimonials carousel dots, developer-section language tabs, pricing Monthly/Yearly toggle + per-card "Start Free" CTA, demo-request "Skip the demo", cta-banner "Developer Portal" link, footer legal links. Probe count: 27 → 1 (the lone remainder is the chatbot bubble caught mid-animation).
  • Pagination dots <44×44 (product-tour, testimonials) — visible dot kept small, hit area wrapped in a 44×44 invisible padding via inner <span> pattern.
  • Register form was toast-only on errors — added the inline role="alert" aria-live="polite" banner + aria-invalid + aria-describedby="register-error" on name/email/password inputs, plus noValidate so the inline message wins over native browser tooltips. Matches the login pattern from Phase 4.

Added

  • Image-alt probe in scripts/mobile-audit.ts (imagesNoAlt + samples). All 4 apps measured: 0 missing alts on every route.
  • Universal :focus-visible rule in playground, developer, and admin globals.css (landing already had it). Keyboard focus is now visible across all apps.
  • Skip-rules in the touch-target probe: ignore display: inline, sr-only, clipped, or elements nested in larger interactive ancestors.

Verification (iPhone 13)

| App | font≥ | hScroll | inputsNoLabel | smallTargets | imgNoAlt |

|---|---|---|---|---|---|

| landing home | 9px (dev pill) | clean | 0 | 1 (anim artifact) | 0 |

| landing other | 11–14px | clean | 0 | 0–2 | 0 |

| playground all | 11px | clean | 0 | 0 | 0 |

| developer all | 13–14px | clean | 0 | 0 | 0 |

| admin all | 13px | clean | 0 | 0 | 0 |

[2026.5-mobile5] – 2026-05-16

Phase 4 mobile audit — keyboard handling, form a11y, responsive toast, skip-to-content, overscroll containment, and a partial marketing-CTA sweep.

Fixed

  • Focused inputs hidden by virtual keyboard — every app's globals.css got html { scroll-padding-bottom: 120px } + input, textarea, select { scroll-margin-block-end: 120px } at <sm. Focus auto-scroll respects these so the field lands clear of the iOS keyboard + autocomplete bar.
  • Toast position collided with mobile chrome — shared Toaster is now a client component that switches bottom-righttop-center below 640px. Landing's direct Sonner usage got a ResponsiveToaster companion with identical logic.
  • Developer login errors were toast-only (F1 violation) — added inline role="alert" aria-live="polite" banner + aria-invalid + aria-describedby on both inputs. Errors now persist visually and for screen readers.
  • No skip-to-content link (A1) — every app's layout.tsx starts the body with a focus-only "Skip to main content" anchor; all <main> elements got id="main-content" + tabIndex={-1} so keyboard / SR users can bypass the nav.
  • Page-level pull-to-refresh fired from inner scrolls — global rule .overflow-y-auto, .overflow-auto, [data-scroll-container] { overscroll-behavior: contain } in all 4 apps. Affects chat history, sidebar nav, tall modal lists.
  • Landing marketing CTAs at &lt;44px (P2-1 in flight) — 5 of the most visible inline CTAs (hero "Try without signup", cta-banner, demo-request, contact-form sales, use-cases cards) bumped to min-h-[44px] sm:min-h-0. Probe count: 52 → 27 on landing home. The remaining ~27 are scattered across other section components; deferred.

[2026.5-mobile4] – 2026-05-16

Phase 3 mobile audit — input ergonomics, tap feedback, touch-target compliance, and PWA installability.

Fixed

  • iOS Safari auto-zoom on input focus — added a @media (max-width: 639px) rule across all 4 apps that forces every form field to font-size: 16px on phones. The page no longer jumps when a user taps an input.
  • No press feedback on mobile — shared Button component now has active: parallels to hover: (scale-[0.98] + color shift) on every variant, plus [-webkit-tap-highlight-color:transparent] to kill iOS Safari's grey overlay. Every app's globals.css also got button, a, [role="button"] { -webkit-tap-highlight-color: transparent }.
  • Touch targets &lt;44×44 — playground header (AV logo, back-link, Sign up CTA), shared Button md size (h-10h-11), suggestion chips on text/image pages, landing announcement X, navbar hamburger, navbar AV logo — all bumped to ≥44×44 on mobile.
  • Tailwind v4 not scanning packages/ui — fixed via @source "../../../../packages/ui/src/**/*.{ts,tsx}" in each app's globals.css. New utility classes used only inside the shared UI package now compile correctly.

Added

  • PWA manifest + apple-touch-icon in all 4 apps. public/manifest.json (standalone display, theme color, icon set) + Next.js metadata.manifest / metadata.icons.apple / metadata.appleWebApp. "Add to Home Screen" now works on iOS and "Install app" on Chrome Android.
  • inputMode + enterKeyHint on chat input (send), email fields (email/next), password fields (go), and name fields (next). Right mobile keyboard pops up; the soft "Enter" key shows the right verb.

Tooling

  • scripts/mobile-audit.ts extended with a touch-target probe (skips inline links, skips elements wrapped in larger interactive ancestors). Reports smallTargets=N plus a top-3 sample of offenders per route.

[2026.5-mobile3] – 2026-05-16

Phase 2 round 2 — eliminated the remaining false-positive on developer auth pages and shipped the actionable P2 polish.

Fixed

  • Developer login + register inputs now have explicit id / <label htmlFor> / autoComplete pairings. Probe goes from 2/3 unlabeled inputs to 0.
  • Landing announcement bar wrapped to 2 lines at 360px — split copy: tight "Text, image, voice, video — one API. Start free" at <sm, longer original at ≥sm. Now single-line on iPhone 13.

Added

  • prefers-reduced-motion: reduce guards in every app's globals.css. Animations drop to 0.001ms, smooth-scroll disabled. Landing also hard-disables aurora / grid-fade / pulse / gradient / typewriter animations.
  • **viewport-fit: cover + env(safe-area-inset-*)** support across all 4 apps. Bottom-fixed elements (cookie consent, back-to-top, chatbot bubble + window) now sit above the iPhone home indicator.

Changed

  • Admin login shield softened — bg-error/8 + text-error/80 reads as "secure" rather than "danger". The vivid red is reserved for the Sign In button below.

[2026.5-mobile2] – 2026-05-15

Phase 2 of the mobile audit — all four P1 issues from MOBILE_UI_AUDIT.md closed the same day as Phase 1.

Fixed

  • Playground 10px caption pills bumped to 11px (the X CREDITS / REQ chip in service-page.tsx and the playground home service-card grid).
  • Playground body + empty-state copy (warning text, file-format hints, "Output appears here…" copy) raised from 12px → 13px on mobile via text-[13px] sm:text-xs.
  • Playground inputs without <label htmlFor> pairing all wired up: image-prompt, video-prompt, voice-text, voice-select, vision-question, plus an sr-only label on the chat input. Probe now reports 0 unlabeled inputs across every service page.
  • Developer + admin 12px metadata text raised to 13px on mobile via a single @media (max-width: 639px) { .text-xs { font-size: 13px } } rule per app — zero desktop impact, covers 30+ text-xs usages per app without touching individual files.
  • Mobile horizontal-scroll backstop extended from landing to playground/developer/admin (html, body { overflow-x: clip; } in each globals.css).

Verification

Smallest visible body font went from 9–12px to 11–14px across all 4 apps. No horizontal scroll on any route at 360–412px viewport widths. Full numbers in docs/MOBILE_UI_AUDIT.md.

[2026.5-mobile1] – 2026-05-15

Fixed

  • Landing horizontal scroll on mobile — added html, body { overflow-x: clip; } as a backstop against rogue absolute-positioned aurora/blur elements that escape their containers. Pages now respect 390px viewport width.
  • Playground header overflowed at 390px — the "Sign up free" CTA was clipped off the right edge. Header now responsively collapses at <sm: subtitle hidden, back-link icon-only, CTA shortened to "Sign up", CreditCounter drops its caption.
  • Admin Sign In button rendered as plain text — the shared Button's variant="danger" used bg-[var(--color-error)] arbitrary syntax which didn't resolve in this Tailwind v4 setup. Pinned the admin login button to bg-error explicitly.
  • Landing /changelog 500 — reverted the page to a 40-line hand-rolled markdown renderer, eliminating the remark runtime dependency.
  • Landing hero typewriter mid-frame clipping — reserved min-w-[5ch] for the cycled word so the surrounding line no longer reflows during the animation.

Added

  • docs/MOBILE_UI_RULES.md — 28-rule mobile UI validation checklist (layout, typography, color, touch, forms, a11y).
  • docs/MOBILE_UI_AUDIT.md — per-screen scored audit of all 4 web apps with P0/P1/P2 issues + per-fix verification screenshots in docs/mobile-audit/.
  • scripts/mobile-audit.ts — Playwright harness that captures iPhone-13 + Pixel-7 screenshots of every route and runs in-page probes (overflow, smallest font, unlabeled inputs).

[2026.5-sprint16] – 2026-05-15

Added

  • Python SDK (pip install agenticvexa) — sync AgenticVexa + async

AsyncAgenticVexa clients mirroring the TypeScript SDK API surface.

  • avx CLI (pip install agenticvexa-cli) — auth login, text,

image, voice, stt, vision, tasks, webhooks tail, status.

  • CHANGELOG (this file) and /changelog route on the landing site.

[2026.5-sprint15] – 2026-05-15

Added

  • Spending budgets & alerts — per-org soft alerts at admin-set %

thresholds (50/80/100 by default). Optional hard caps refuse deductions

past the cap. GET/PUT /v1/billing/budget.

  • Cost forecastingGET /v1/billing/forecast returns end-of-period

projection from 7-day rolling rate, with per-service breakdown.

  • Plan recommendationGET /v1/billing/recommended-plan suggests the

cheapest plan that fits projected 30-day usage.

  • Developer onboarding wizard — first-login banner on /dashboard tracking

"create key / make first call / invite team"; auto-dismisses when done.

[2026.5-sprint14] – 2026-05-15

Added

  • GDPR data exportPOST /v1/users/me/data-export triggers a Celery

job that compiles profile + orgs + 365d API logs + ledger + invoices + 90d

webhook deliveries into a JSON file; emails a 7-day download link.

  • Audit log search + CSV export/v1/admin/audit-logs filters by

actor/action/target/date/substring; sibling .csv endpoint streams matches.

  • Status page email subscriptions — opt-in via POST /v1/status/subscribe

with double-opt-in confirmation; fanned out on every incident create/update.

  • Webhook signing-secret rotationPOST /v1/webhooks/{id}/rotate-secret

returns the new secret while keeping the previous valid for 24 hours;

deliveries dual-signed during the window (X-AgenticVexa-Signature: v1=new,v1=old).

[2026.5-sprint13] – 2026-05-15

Added

  • Self-hostable Docker Compose (docker-compose.selfhost.yml) — api +

worker + beat + Postgres + Redis. docs/SELF_HOSTING.md covers seed/backup/

scale-out/upgrade runbooks.

  • Public status pagestatus_components, status_incidents,

status_incident_updates tables; /v1/status (public) +

/v1/admin/status/* (write).

  • Stripe scaffold — code-complete payment provider; activates only when

STRIPE_API_KEY is set, otherwise the /v1/webhooks/stripe route returns 503.

[2026.5-sprint12] – 2026-05-15

Added

  • WebSocket text streaming at /v1/ai/text/stream?token=<jwt> — delta

frames over JSON-lines; charges credits upfront, refunds on upstream failure.

  • MCP server (apps/mcp/server.py) — exposes 5 AgenticVexa tools to

Claude Desktop / Cursor / Continue via stdio.

  • TypeScript SDK (@agenticvexa/sdk) — hand-written, Node 18+ and

browser-compatible; sync calls + async tasks.wait() + text.stream()

async generator.

[2026.5-sprint11] – 2026-05-15

Added

  • Developer webhooks page — full CRUD, one-time signing secret reveal,

per-endpoint delivery log with auto-refresh, test-ping.

  • Developer referrals page — code, share URL, signup/conversion/credits

stats, anonymized top-10 leaderboard.

  • Admin AI providers page — catalog + per-service route chain with

priority reorder.

  • Admin customer health page — red/yellow/green filter, score bar,

factor breakdown.

[2026.5-sprint10] – 2026-05-15

Added

  • Maintenance Redis cache + pub/sub — admin flips publish on

maintenance.flip; in-process cache invalidated across all API instances

in ~10ms.

  • Playground async UI — image/voice/stt/vision pages POST to /async and

poll /v1/tasks/{job_id} by default.

[2026.5-sprint9] – 2026-05-15

Added

  • DB-driven provider catalogai_providers + ai_provider_routes

tables; per-service fallback chain managed at /v1/admin/providers.

  • JWT RS256 + JWKSJWT_ALGORITHM=RS256 enables asymmetric signing

with rotation grace window; JWKS at /.well-known/jwks.json.

[2026.5-sprint8] – 2026-05-15

Added

  • Log table partitioning helpersapp/db/partitioning.py + monthly

beat creating next-month partitions. Cutover runbook at

docs/PARTITIONING_RUNBOOK.md.

  • Maintenance auto-flip — 1-minute beat flips enabled at

scheduled_start/scheduled_end.

  • Customer health scorecustomer_health_snapshots table; daily

beat scoring on 5 signals (volume, errors, recency, trend, stickiness).

  • Invoice PDF renderingGET /v1/billing/invoices/{id}/pdf streams a

reportlab-rendered PDF with GST split for INR.

[2026.5-sprint7] – 2026-05-15

Added

  • Redis hot credit counter — Lua-atomic deduct + 30s reconciler beat;

removes SELECT FOR UPDATE from the hot path.

  • Webhook delivery workerwebhook_deliveries table, HMAC-SHA256

signing, exp-backoff retries (1m→3d), DLQ at 8 attempts, /v1/webhooks CRUD.

  • Outbound provider rate limiter — Redis token-bucket per (provider,

service); raises 503 UpstreamRateLimitExceeded after a 5s grace.

  • get_org_owner Redis cache — 10-min TTL; invalidate on

ownership/email/name change.

[2026.5-sprint6] – 2026-05-15

Added

  • Lifecycle drip — daily beat fires welcome day 1/3/7 and 7-day

re-engagement emails based on users.created_at / last_login_at.

  • Referral programreferrals + referral_redemptions tables, code

generation, paid-conversion 500-credit reward, /v1/referrals/{me,leaderboard}.

[2026.5-sprint5] – 2026-05-15

Added

  • Per-key abuse detection — hourly beat, 3σ over 7-day baseline →

auto-disable key + audit log + apikey.exposed.github_scan email.

  • Failed-login throttling — 5 fails / 15 min / IP → 15 min Redis lockout

(fail-open if Redis is unreachable).

[2026.5-sprint4] – 2026-05-15

Added

  • structlogrequest_id / user_id / path / method auto-bound.
  • Prometheushttp_requests_total, http_request_duration_seconds,

credits_deducted_total, ai_jobs_total, email_sends_total,

active_subscriptions; /metrics endpoint.

  • Grafana dashboard at infra/grafana/agenticvexa-api.json (7 panels).

[2026.5-sprint3] – 2026-05-15

Added

  • Async AI proxyai_jobs table; POST /v1/ai/{service}/async (202) +

GET /v1/tasks/{job_id} polling. Credits refunded on worker failure.

[2026.5-sprint2] – 2026-05-15

Added

  • All Razorpay billing email events wired (renewed/upgraded/downgraded/

suspended, payment.failed, 3 dunning_retry stages).

  • Dunning scheduler — daily 09:00 IST beat; D+1/3/7 retries + D+10

auto-suspend.

  • Subscription upgrade/downgrade with proration — upgrade immediate +

invoiced, downgrade takes effect at period end.

[2026.5-sprint1] – 2026-05-15

Added

  • Alembic baseline migration + app.bootstrap for fresh DBs.
  • Celery worker + email dispatch auto-failover (asyncio.create_task

email_send.delay() with fallback).

  • Email retry sweep every 15 minutes.
  • Redis User cache (5-min TTL) on JWT validation; invalidate on mutation.