What we shipped.
A running record of the platform work that matters to agent builders and cohort users — newest first.
Each release block lists what changed and why. Releases that ship a new SDK version get their own permalink at /changelog/<slug> with version chips linking to the npm or PyPI page; in-page anchors stay reachable as #r-<date>. Every PR with user- or builder-visible impact lands a Fixed / Added / Changed / Security / Infrastructure entry here — enforced by CI.
Updated
Analytics stops following you past the marketing site
- Changed
Third-party pageview analytics is gone from the marketing site entirely. No script is loaded on any page, so the "no tracking pixels" commitment on
/securitynow holds without qualification rather than depending on how a tracker is configured. Conversion measurement moves to counters the API already maintains server-side, which are unaffected by ad blockers and never see a visitor's browsing. - Security
Pageview analytics no longer records anything once you sign in. The marketing site loads Umami cookielessly, but Umami's default auto-tracking patches
history.pushStatefor the whole document, and the script outlived the marketing layout — so a client-side navigation into/onboardingor/dashboardkept reporting authenticated page paths, including ones carrying agent and mailbox ids. Auto-tracking is now off and pageviews are reported explicitly, only for routes that resolve through the marketing layout. This closes a gap against the "no tracking pixels" commitment on/security; the dashboard was never meant to be in scope. - Fixed
The analytics service survives being rescheduled. Its startup ran a database preflight that exited immediately rather than retrying, so whenever the app and its database moved to a new node together, the app lost the race and crash-looped instead of waiting — briefly taking
analytics.agentdraft.iooffline. Startup now waits for the database to accept connections. - Infrastructure
The analytics database is no longer a candidate for routine cluster scale-down, and a
PodDisruptionBudgetmakes a voluntary eviction of its single replica stall visibly instead of silently dropping analytics. - Fixed
Signups that start from a comparison or alternatives page are attributed again. Those pages tag their "free audit" call to action with a
srcmarker, but the audit tool dropped it on the way to sign-in and substituted a fixed marker of its own — so fifteen of the nineteen tagged entry points reported nothing, and the server-side funnel could not tell which page had earned a signup. The tag now travels through to sign-in and survives the magic-link round trip. - Security
The signup funnel's per-source breakdown can no longer be flooded with junk. A marketing call to action tags its link with a
srcmarker that the sign-in endpoint stores as part of a counter's key — but the endpoint is public and accepted any string, so a caller could mint unlimited counter rows and bury the real attribution under noise. Unrecognised markers are now grouped underother, holding a day's breakdown to a fixed size.
- Changed
Blog ingest accepts updates and deletes, not just creates
- Added
PUT /v1/blog/posts/{id}andDELETE /v1/blog/posts/{id}now exist. The ingest API was create-only, so a publisher correcting an already-published post got a404— which its own error handling read as "this content was rejected" rather than "there is no route here", flipping a live, correct post to a failed state it could never repair. - Fixed
Both write paths re-materialize the affected static blog page before returning, so a corrected post is visible to readers and crawlers immediately instead of waiting for the next frontend deploy. A delete removes its page rather than leaving it served.
- Fixed
Materialized blog pages keep the site masthead, nav, and footer.
emitBlogShell()blanked the entire#appsubtree when cutting the publish-time shell, andMarketingLayout— which owns<nav>and<footer>— lives inside it, so every page the ingest API wrote went live with only the article: 10 links instead of 52. It stayed invisible while pages were create-only, because the next frontend deploy re-rendered over them; addingPUTmade materializer output long-lived and the gap permanent. The shell now cuts at the layout's<main>, so the chrome survives. - Fixed
A materialized post or index page no longer emits a second
<main>landmark inside the layout's own — the same one-landmark rule/docsalready follows, so "skip to main content" stays unambiguous. - Security
Pinned
nanoidto 3.3.17 through an override. The advisory (GHSA-2v37-7h3g-55p8, custom generators can loop indefinitely on a zero size) landed against the version Vite pulls in transitively via PostCSS, which turned the CI dependency audit red on commits that were green when they were cut. - Fixed
Entitlement reads and dunning updates now use centralized DynamoDB key builders, and a failed invoice cannot create a deadline-only row for an org without an entitlements record. A first-invoice decline for such an org is acknowledged rather than erroring, so Stripe no longer retries the event for days.
- Added
One redirect path for a stored paid-tier signup
- Changed
The pricing page's comparison table, tier bullets, and FAQ prose no longer advertise
Calendars / seat,Multi-agent priority, orAudit retention— none had an enforcement call site inapp/— and the table now lists only entitlements the backend actually checks alongside the free tier's 3 agents. - Changed
The pricing page's comparison table also drops the
SSO / SCIMandWhite-label endpointsrows and their matching Enterprise-tier bullets: like the three rows removed above,ssoandwhite_labelare declared on theEntitlementsdataclass but read nowhere else inapp/, so they had no enforcement call site backing the claim either. - Changed
Onboarding now starts Stripe Checkout for a stored paid-tier intent through a single shared helper instead of duplicating the flow per page. A failed checkout call now lands on Billing with a
checkout_errormessage rather than leaving the tab on a blank redirect. - Changed
The Stripe webhook now handles
invoice.payment_failed: a failed charge starts a 7-day grace period instead of silently keeping paid entitlements until Stripe eventually cancels, and a subsequent successful payment clears it. - Infrastructure
Added a read-only
scripts/stripe_account_audit.shand inventoried the live webhook endpoint and account branding/business profile ahead of the billing-repair work, so the minimal Stripe-side change set (currently: none) is documented before anything mutates. - Changed
Confirmed the live Stripe webhook endpoint's
enabled_eventswith a real API write, re-assertingcheckout.session.completed,customer.subscription.updated,customer.subscription.deleted, andinvoice.payment_failedtogether so dunning-relevant event coverage is documented and verified rather than assumed. - Fixed
Dry-ran the exact webhook
enabled_events, Files API logo/icon upload, andbusiness_profile/settings.brandingmutation calls against a non-production Stripe account before touching live, catching that standalone-account branding self-updates are API-rejected and must go through the Dashboard instead. - Infrastructure
Live Checkout branding's
settings.branding.icon/.logostill point at a straygoogle-oauth-logo.pngfile instead of an AgentDraft mark, andbusiness_profile.name/product_descriptionare wrong/unset — not yet fixed for customers — after uploading a correctapple-touch-icon.pngas new, unattachedbusiness_icon/business_logofiles and confirming Stripe blanket-rejects (403) self-service account writes on this standalone account, so attaching them needs a human via Dashboard using the values recorded indocs/stripe-account-audit.md. - Fixed
The Billing page now shows an explicit
Developerplan card ($0/forever) and a7 days — developeraudit-retention footer instead of silently falling through to the Enterprise arm's$5k–$20k / yearcopy for free-tier accounts. - Changed
Free-tier accounts on the Billing page now get working
Upgrade to IndividualandUpgrade to Teambuttons that start Stripe Checkout directly, replacing the disabledOn enterprisebutton every Developer-tier user used to see as their only upgrade affordance. - Changed
The Billing page now reads the
checkout_errormessage left by the onboarding fallback redirect and shows it in a visible banner above the upgrade buttons instead of silently dropping it, clearing the parameter from the URL afterward so a refresh doesn't re-raise it. - Changed
Locked in regression coverage for Enterprise checkout being unreachable by construction:
billingApi.checkout's tier parameter accepts onlyindividualandteam(anenterprisecall failsvue-tsc --noEmit), and the/pricingpage's Enterprise CTA is now tested to open themailto:sales@agentdraft.iocontact-sales flow and issue no request to the checkout endpoint. - Fixed
Removed the marketing masthead's
r/indiehackersgiveaway banner (a year of Team free), which was undercutting the price this billing work exists to charge. The bulletin strip's status/edition/build-date meta still renders. - Changed
Finishing the onboarding template step with a stored paid-tier intent now drops the visitor straight into Stripe Checkout for that tier instead of routing them to Billing to click upgrade again; with no stored intent, onboarding still finishes on the Rules page.
- Changed
An already-onboarded user who verifies with a stored paid-tier intent now lands straight in Stripe Checkout for that tier instead of Billing; users still mid-onboarding, or with no stored intent, keep their existing destination.
- Changed
The dashboard usage endpoint now returns a nullable
dunning_grace_untilfield with the org's stamped card-failure grace deadline, so the Billing page will be able to warn a user before their paid tier lapses. - Changed
The Billing page now shows a
Payment failed — update your card within N dayswarning whiledunning_grace_untilis set and still in the future, so a lapsed card's grace window is visible before the downgrade happens instead of after. - Fixed
Free
Developer-tier accounts can now open Stripe Checkout to upgrade, and the Billing page no longer reports Enterprise pricing when a free-tier account loads it. - Changed
The
/pricingcomparison table no longer listsCalendars / seat,Multi-agent priority, orAudit retention— none had a backing entitlement check — and the freeDevelopertier is now stated as 3 agents instead of 1.
- Changed
Free tier now representable in the billing client
- Fixed
The frontend
Tiertype now includesdeveloper, matching the tier every new org actually resolves to on the backend — billing UI can represent the free tier instead of only paid ones. - Changed
The Developer tier's pricing-page copy now states
3 agentsinstead of1, so a free workspace can actually try multi-agent priority resolution rather than being capped before it can use it.
- Fixed
Free tier can now run multiple agents
- Fixed
The free Developer tier's agent cap is now 3, not 1 — a free workspace can actually run more than one agent, which is what multi-agent priority means.
- Fixed
Retired marketing URLs now resolve cleanly
- Fixed
Old calendar, agent-mailbox, and framework-guide URLs now permanently redirect to their current canonical pages instead of serving the noindex 404 shell.
- Added
A public
/contactpage now routes product, billing, enterprise, security, press, and partnership questions to the right channel. - Fixed
Documentation and glossary links with a
#sectionnow scroll to the requested section instead of always returning to the top of the page.
- Fixed
Approvals — a human sign-off gate for any agent action
- Added
Workspaces can have more than one person in them. A new Team page invites teammates by email; they accept, land in your workspace, and share its plan, billing and calendars. Team includes five seats, Enterprise unlimited — the
seatsnumber on the pricing page previously bound nothing, because there was no way to add a second person at all. - Changed
Seats are capped per tier. A pending invite holds its seat until it's accepted, revoked, or expires after 72 hours, and inviting past the cap returns
402 quota_exceeded. An address that already has its own AgentDraft workspace can't join another one. - Security
Billing (checkout, portal, plan changes) and the webhook signing secret are now owner-only. Everything else in a workspace stays shared, which is what a seat is for — but a credential that signs every delivery, and the payment method, are not.
- Changed
The per-tier mailbox count is now enforced, not just advertised. An agent receives mail only while it holds one of your plan's inboxes — Developer and Individual include one, Team five, Enterprise unlimited — so Individual's three agents share a single mailbox rather than quietly getting three. Agents past the allowance are created normally and still book, hold and audit; they have no address until a slot frees up.
- Added
POSTandDELETE /v1/dashboard/agents/{id}/mailboxmove an inbox between agents, so a finite pool of mailboxes is usable across a larger pool of agents. Enabling one past the cap returns402 quota_exceeded, andGET /v1/dashboard/billing/usagenow reports mailbox consumption. - Changed
mailbox_addressis now nullable on the agent APIs andGET /v1/mailbox/mereportsinbound_enabled, so an SDK can tell that an agent has no inbox instead of caching an address that silently drops mail. Agents created before this change keep their inbox. - Fixed
Corrected calendar-provider claims across the site. Several pages said AgentDraft connects to Microsoft 365, Apple iCloud, Fastmail and generic CalDAV; those are waitlist-only, exactly as the dashboard's connect screen has always shown. Google Calendar, Folio and read-only ICS are what ship today, and the rest are now described as roadmap.
- Fixed
The Team tier no longer advertises "private beta · no card required". Starting Team opens a live Stripe subscription checkout at $25/month, and always has.
- Fixed
The pricing FAQ said Enterprise "adds" SSO, SCIM, white-label endpoints, a dedicated email IP and a custom-domain mailbox. None of those are built; the comparison table on the same page already called them pilot-scoped, and the FAQ now agrees with it.
- Fixed
The public collision benchmark now publishes a real measured run instead of a hand-authored reference fixture. Its correctness metric also no longer counts priority bumping as a double-booking — a legitimate bump chain produces several
201s in one round, which scored a healthy engine at 22% accuracy. Latency figures are now labelled with the stack they were measured on. - Fixed
The blog publish guard matched blocked phrases as bare substrings, so
SSOfired insidesubprocessorand a truthful DPA post was rejected with a422. Phrases now match on word boundaries. - Added
Agents can pause for a human on any consequential action with
POST /v1/approvals— a deploy, a payment, an outbound email — attaching an evidence payload and pollingGET /v1/approvals/{id}for the decision. Requires the newapprovals:requestscope, which is off by default on new keys. - Added
New Approvals queue in the dashboard: action-type badge, the agent's evidence pretty-printed and collapsible, an expiry countdown, and an optional note that reaches both the agent and the audit log on approve or deny.
- Added
Four subscribable webhook events —
approval.requested,approval.approved,approval.denied,approval.expired. Payloads carry the decision, never the evidence blob. - Added
client.approvals.request(),.get(), and.wait()in both the Python and TypeScript SDKs;wait()returns on any terminal status and follows the request's own expiry so it can't hang forever. - Added
Email notification when an agent is waiting on you, throttled to one message per five minutes with the pending count, and switchable off under Settings → Notifications.
- Added
Approvals now explains itself. The queue carries a collapsible "How approvals work" panel — how to grant
approvals:request, what an agent sends, what your note does, and why an expiry counts as a no — and the docs gain a full § 06 Approvals section with runnable Python, TypeScript and cURL examples, a status table, and the field limits. - Fixed
Approval requests carrying a long unbroken value — a URL, an object key, a token — no longer stretch the approvals queue sideways; long summaries and evidence now wrap in place, on phones included.
- Fixed
The approval note field now shows a focus ring when tabbed to, and the Settings notification link is underlined rather than distinguished by colour alone.
- Changed
Approvals is now the fourth leg of the product description —
inbox + calendar + approvals + audit— across the site metadata, the A2A agent card, the ChatGPT app manifest, and the public capability contract at/.well-known/product-capabilities.json, which gains ahuman_approvalsentry plus guardrails marking Slack/email approve-from-chat and approval policy engines as not-yet-shipped. - Changed
A malformed blog-publish payload now returns
400 invalid_payloadinstead of422. On the publish routes422is reserved for capability-claim rejections, so publishing clients can tell a broken request from a blocked claim. - Fixed
The capability-claim guard now recognises
is/areas asserting a claim, and matches a phrase through the ways HTML mangles it — a line break, a , an inline<em>. Copy like "AgentDraft is open source" is caught rather than published, while comparison copy that names a competitor's feature and denies it of AgentDraft still publishes.
- Added
Cookieless marketing analytics goes live
- Infrastructure
Deployed the self-hosted Umami instance behind
analytics.agentdraft.io— cookieless, no cross-site identifiers, marketing pages only; the dashboard stays untracked.
- Infrastructure
Answer-engine polish and a shorter path to an API key
- Added
Glossary entries for agentic email, mailbox quota, and human-in-the-loop gate, cross-linked into the email guides.
- Added
Blog posts rendered by the publish pipeline now declare speakable JSON-LD and can include structured comparison tables.
- Changed
The glossary, comparison pages, and email-flow monitoring guide now state free-tier pricing up front with direct sign-up CTAs.
- Fixed
Replaced dead citation links in three blog posts with verified primary sources.
- Fixed
Corrected a blog post that rendered unconverted markdown emphasis as literal asterisks.
- Fixed
The Nylas comparison page no longer mixes two different Nylas pricing tiers when arguing AgentDraft is cheaper.
- Added
The blog claim guard checks claims, not keywords
- Fixed
The capability-claim guard no longer rejects a post just for containing a word like
self-hosted,SOC 2, orSAML. It now checks whether the sentence *asserts* the capability of AgentDraft: truthful disclaimers ("AgentDraft is not self-hosted", "SSO is on the roadmap, not available today", "we do not hold SOC 2") publish, while over-claims are still blocked. Previously the exact wording the guard itself recommended could be rejected. - Changed
The guard's paraphrase lexicon was widened so over-claims that dodge the original keywords are still caught (e.g. "run AgentDraft on your own servers", "Okta/Azure AD single sign-on", "Outlook sync"), and the public
/.well-known/product-capabilities.jsoncontract reflects the expanded terms. - Added
The guard now rejects the false claim that AgentDraft ships load-testing or throughput stress-testing tools for your architecture (it publishes only a conflict-engine correctness benchmark). Phrases like "benchmarking tools to stress-test your architecture" and "simulate high-volume traffic" are blocked, while posts about the real public benchmark still publish.
- Fixed
The blog publish guard now allows glossary-style SEO mentions like
SAML,SOC 2, orOutlookwhen the sentence does not claim AgentDraft supports, holds, or ships that capability. - Changed
The public docs now spell out the AgentDraft mailbox inbound size limit: messages over
40 MBare rejected at SMTP with552 5.2.3, so monitors should send summaries plus artifact links instead of retrying oversized MIME bodies.
- Fixed
No more dead citation links on the blog
- Fixed
The blog publishing integration now strips ephemeral Vertex
grounding-api-redirectcitation links at ingest. These AI-generated source links expire within days and were surfacing as broken outbound links on published posts; they're now removed automatically, so re-publishing a post self-heals any dead citation. - Fixed
The blog capability-claim guard now only flags a blocked term (open source, SOC 2, ISO 27001, etc.) when the same sentence names AgentDraft. Educational posts discussing third-party open-source frameworks or telling readers to vet a vendor's certifications are no longer wrongly rejected, while genuine false claims about AgentDraft are still blocked.
- Fixed
Every published blog post is back in the sitemap
- Fixed
The sitemap now lists all published blog posts. The build-time query stopped at DynamoDB's 1 MB page limit and silently dropped the oldest posts; it now paginates to exhaustion so search engines can discover every article.
- Added
The email-flow monitoring page now links to Nightlamp, the first-party deployment that runs its email-dependent checks on AgentDraft mailboxes.
- Changed
The blog publishing integration now rejects posts that assert capabilities AgentDraft doesn't ship (open source, self-hosting, SSO, Microsoft 365 sync, compliance certifications) with a structured
422, and the public/.well-known/product-capabilities.jsoncontract lists those blocked claims so external SEO tooling stops generating them.
- Fixed
Answer-engine remediation pass
- Fixed
Blog, legal, and alternatives pages now expose clearer short-answer blocks, question-led sections, and FAQ schema for answer engines.
- Fixed
DNSCove authoritative DNS cutover
- Infrastructure
Production DNS and cert-manager DNS-01 automation now target DNSCove while AWS Certificate Manager continues to issue CloudFront certificates.
- Infrastructure
Older releases
- Blog ingest API returns the real post URL
- Free calendar collision audit, right from the comparison pages
- Try the API with no signup
- Instant API key, plus a code fallback for magic links
- A faster path from CTA to API key
- Alternatives guides, glossary expansion, and benchmark census
- Trust pages, SEO metadata, and production API tuning
- Billing correctness and an open-source collision benchmark
- A guided setup checklist and a clearer, more accessible dashboard
- Send feedback from the dashboard, plus blog & security SEO fixes
- Checkout now collects tax and accepts promo codes
- Calendar list no longer breaks after disconnecting a feed
- Webmail calendar connector is now Folio
- Calendar connect surfaces real errors; local dev boots again
- Billing webhook resolves the correct plan
- Private app routes are easier for crawlers to retire
- Google user data disclosures
- Launch-readiness proof is easier to audit
- Dashboard sessions last through the workday
- Publish and webhook setup feedback is clearer
- Mailbox webhooks can target one agent
- Email-flow monitoring with AgentDraft mailboxes
- Block out your vacations
- Counter-proposals documented, plus API reference fixes
- Webhooks are now documented
- Booking notes and the video link now land on the calendar event
- The timezone picker is now searchable
- Set your timezone — the one that actually gates bookings
- SDKs speak title, invitee, and timezone
- Scheduling rules are actually enforced
- Working hours read in your own timezone
- Pricing answers its own questions
- Bookings know who they're for
- One canonical URL per marketing page
- Connected calendars resync themselves
- The glossary doubles in size
- Integration pages for n8n and the OpenAI Agents SDK
- A sharper share card and a more legible masthead
- The free Developer tier reaches the homepage
- A free Developer tier, a mobile nav, and a clearer homepage
- Blog posts published via the ingestion API render cleanly
- Recurring ICS events with a non-UTC UNTIL now expand fully
- Permanently delete revoked agents
- Lowercase-safe agent mailbox routing
- Mid-run human consent for high-impact agent actions
- Marketing claim audit and pricing copy alignment
- Conferencing links, Calendar Audit, ChatGPT App, coordination-layer pillar
- Dashboard Webhooks page
- P1 differentiation moat — multi-agent negotiation + OSS engine reference
- P0 distribution oxygen — A2A protocol, two more /vs pages, Vercel AI + Mastra adapters, Claude skill
- Footer composition + Field notes in masthead nav
- Launch wave 1 — collision benchmark, vs/temporal-cortex, newsletter capture
- SEO landing pages + outbound mail fix
- Launch-week polish
- Passkeys
- A real changelog
- Launch readiness — SDKs, packages, and discovery
- Calendar connectors, agent key lifecycle, mailbox API
- Agent mailboxes, onboarding, dashboard hardening
- Production architecture and deploy pipeline
- Initial AgentDraft platform
Frequently asked
What gets a changelog entry?
Anything users or agent builders can observe — new endpoints, breaking changes, dashboard UX, fixed bugs, security patches, infrastructure work that touches the public plane. Internal-only refactors and tests opt out with the skip-changelog label. CI fails any other PR that doesn't touch this changelog file.
How do per-release permalinks work?
Releases that ship a new SDK version get a dedicated /changelog/<slug> permalink with a short summary, version chips, and SoftwareApplication JSON-LD. In-page anchors stay reachable at #r-<date> for releases without a permalink — if multiple un-slugged releases share a date, the second and later get a numeric suffix.