Changelog
What changed on the surface you integrate against: the public API, token format, standards support, and SDKs. Internal dashboard work is not listed here unless it changes something you call.
Dates are the date the change went live on the development environment (testapi.axowl.com).
2026-08-17
Section titled “2026-08-17”POST /api/org/{slug}/app-groups/{groupId}/data-sources/{id}/credentials — read back a connection’s credentials.
Provisioning a Strix database handed you a password exactly once, in the create response, and
nothing could show it again — so a database you owned could become one you had no way to reach.
This route returns the host, user, password and two ready-to-paste forms (connectionString for
ADO.NET, connectionUri for psql and most ORMs). It works for external connections too.
It requires a personal seal, bound to this specific data source and to the reveal action, so
a seal taken for one database cannot be replayed against another. Every successful call emits
appgroup.datasource.credential_revealed — this is the only path by which a stored password
leaves the system in plaintext, and the record exists so that question has an answer later.
POST …/data-sources/{id}/rotate-password — reissue a Strix password.
Reissues the managed role’s password and stores the new one. Same seal requirement, bound to
rotate rather than reveal. The previous connection string stops working immediately —
anything using it has to be updated with the value this call returns. Managed databases only;
external connections return NOT_MANAGED, because their credentials are not ours to change.
Emits appgroup.datasource.password_rotated.
Strix usage is now readable — GET /api/org/{slug}/settings/usage and the data-source listing.
Consumption has been metered and billed since 2026-08-05, but nothing exposed it, so the first
time you learned what a database cost was the invoice. The org-level route now returns
strixGbMonths, strixCuHours, the amounts they come to, and the published rates; the
data-source listing carries the same figures per database, so “which one is costing this”
has an answer.
Two things the response is careful about. strixMinimumDueUsd is the gap to the monthly minimum,
not a surcharge — it falls to zero once usage passes the floor. And strixMetered /
usageMetered distinguish “measured, and it is zero” from “not measured yet”: consumption is read
from the provider every few hours, so a database created moments ago legitimately reads zero.
Figures are the current month in progress, while billing settles the previous month.
PATCH …/data-sources/{id} — edit a connection instead of replacing it.
Until now the only way to change a password or a host was to disconnect and register again, which
broke the record of how long that connection had been in place. Sends only what changes; an absent
or empty password means unchanged, not cleared. Connection fields are editable on external
connections only — for Strix, host and user are ours to keep correct, so only name applies.
Connection-affecting edits are tested before the response returns, and the call emits
appgroup.datasource.updated carrying the names of the changed fields, never their values.
See Database management.
2026-08-14
Section titled “2026-08-14”GET /api/v1/admin/audit/behavior/rollup — visitor behavior aggregates.
The behavior beacon shipped with somewhere to send events but nowhere to read them back. This
route closes that: pages, dwell time, referrers, campaign sources and countries for an app group,
from sealed daily rollups. Takes orgId, days (1–90) and an optional appGroupId.
There is no add-on gate — the full 90-day window is free. Behavior analytics is metered on collection, not on looking at it, which makes it a different axis from Audit Analytics and its 3-day preview clamp.
Two things the response says out loud rather than papering over. Visitor counts are named
visitorsUpperBound / identifiedUsersUpperBound because rollup rows are keyed by page, referrer
and country — one visitor reading three pages is counted once per page. Views and dwell time are
exact. And days without a rollup are omitted rather than drawn as zero, with coveredDates versus
requestedDates telling you how much of the window is real.
Each daily rollup emits a sealed anchor (audit.rollup.behavior_created) carrying the file’s
SHA-256 and a hash of its source list. Individual page views are not sealed — at that volume it
would be neither affordable nor useful — so the integrity claim is made once a day at the
aggregate: this count was computed over rows that have not been altered.
2026-08-13
Section titled “2026-08-13”You choose what a session clash does — prompt on the owner gate.
Handing someone over with login_hint used to have exactly one behaviour when the browser
held a different Axowl session: stop, explain the clash, and make them pick. That screen is
a wall for the common case, because the hint came from your own session — you already know who
they are. The gate now takes prompt, in OIDC’s grammar: omitted (or login) honours the
hint — Axowl signs that browser out of the other account and opens the form as the hinted
address, saying on the card which session it ended — and select_account keeps the old
chooser, signing nothing out until the person picks. An unrecognised value is treated as the
default: a hint is an explicit instruction, and dropping it because a parameter did not parse is
the worse failure. Axowl.Sdk.Identity.Abstractions carries the type as OwnerGatePrompt with
AxowlOwnerGate.BuildLoginUrl(...), so the parameter names live in one place. Existing links
keep working; they move from “ask” to “honour the hint” unless you send select_account.
See The account-owner gate.
2026-08-10
Section titled “2026-08-10”Seal who acted — actor_connected_id on the seal API.
A seal used to record what happened but had no place for who did it: the SDK authenticated
with an org API key, so a customer’s action sealed by your backend left no actor on the
anchor. SealRequestProto (and the REST transcoding shape) now takes an optional
actor_connected_id plus an actor_is_employee claim; the .NET SDK exposes it as
SealActor on SealNewAsync / ReSealAsync. The actor is always a ConnectedId —
end customers hold badges too — the server verifies the badge belongs to your organization
and that the employee claim matches its subject axis, and the claim joins the hashed input
so it cannot be swapped after the fact. Requests without an actor are unchanged, byte-for-byte.
This closes the identity half of billing your own customers on Axowl: identity from
introspect, attribution in the seal.
See Integrity SDK.
2026-08-06
Section titled “2026-08-06”IEntitySealer — edit a sealed record without breaking its seal.
Sealing used to be a one-shot call at creation: the SDK gave you SealAsync and left the chain
bookkeeping to you, so a record could be sealed but never legitimately changed. Editing a field
inside the sealed payload made the row read as tampered, and the only alternative was to keep the
interesting fields out of the seal entirely. IEntitySealer closes that: implement ISealedRecord
on the entity, call SealNewAsync on create and ReSealAsync after an edit, and each version
claims the next chain position — so Axowl holds one anchor per version and the edit is recorded,
rather than hidden. Registered automatically by AddAxowlIntegrityClient(...), along with a
default Sha256Hasher. Note the chaining rule it enforces for you: position n links to the
sealedHash the server returned at n − 1, not to your own recordHash.
See Integrity SDK.
Unpaid subscriptions now downgrade instead of running on indefinitely. A failed plan charge used to produce one low-balance notification and nothing else, so a plan could stay active without ever being paid for. A shortfall now opens a 3-day grace period — owners get an in-app notification and an email naming the exact deadline — and the plan drops to Free if it is still unpaid when the grace ends. Topping up at any point clears the clock, and a later shortfall starts a fresh one. Free organizations have nothing to downgrade and Enterprise agreements are settled contractually, so both are exempt; add-ons are not disabled by a downgrade. Your data is never touched. See Invoices.
2026-08-05
Section titled “2026-08-05”POST /api/org/{slug}/app-groups/{groupId}/data-sources/strix — provision a managed database.
Creates a Strix (sealed Postgres) instance and registers it as a managed data source, sealed from
the first row. Body: { name, region? }, where region falls back to the organization’s home
region. Requires the Strix integration to be active.
See Database management.
Data source listings now carry Region and ProviderRegion.
Region is two axes — where the organization is homed, and where a given provider actually runs the
instance — so a listing tells you both rather than implying they are the same.
See Database management.
2026-08-04
Section titled “2026-08-04”GET /api/v1/admin/audit/analytics and /analytics/rollup — aggregated audit analytics.
Usage aggregates, plus sign-in outcomes and event volumes read from sealed daily rollups. Rollups
are built once a day just after midnight UTC and backfilled from archived raw events, so
coverage reaches as far back as your raw history — not just from the day you enabled it. Both routes
work without the Audit Analytics add-on over a 3-day preview window: the server clamps days to
3 and says so with "addonActive": false, "freeWindowDays": 3. The add-on opens the full 90 days.
See Audit.
POST /api/org/{slug}/endusers/bulk-import — up to 500 end users per call.
Body { appGroupId, users: [{ email, displayName? }] }. Each row goes through the same creation path
as a single create — created Pending with an org identity, granted app access, sent a Magic Link
invite in the background. Returns per-row created / duplicate / invalid.
See End users.
enduser.security.login_failed — new event.
Emitted on every rejected end-user sign-in. If you subscribe to event patterns on an outbound
webhook, this one is new traffic. See Events.
GET …/data-sources/{id}/coverage — integrity coverage for a connected database.
Reports hashes and row counts for a database you registered read-only. Contents stay yours — the
check reads counts and hashes, not values. See Database management.
Withdrawals now disclose the cost-recovery deduction. Withdrawing prepaid cash returns it as a refund to the original card payment, minus 2.9% + $0.30 — the processing fee the payment processor does not return on refunds, recovered at cost with no margin — plus a proportional clawback of any volume bonus that rode in with that top-up. Both appear as their own ledger lines. See Invoices.
2026-07-27
Section titled “2026-07-27”Outbound webhooks — Axowl can now call you.
Register HTTPS endpoints under /api/org/{slug}/webhook-endpoints, subscribe them to event
patterns, and receive signed deliveries. Signatures follow
Standard Webhooks, so an existing Stripe/Svix verification
library works unchanged. See Webhooks.
POST /api/public/v1/orgs/owner-status — is this person an Axowl account owner?
Answers no_owner_account / owner_account_exists plus the organizations they own. The e-mail is
read from the Bearer end-user token, not the request body — a body parameter would let any
ah_live_ holder probe which e-mails have accounts. See Owner gate.
Fixed — ready was always false on organizations.
The flag that tells you an organization can actually sign was reading a column nothing ever wrote,
so every organization looked unusable. It now derives from the owner’s real credential.
See Public API.
Fixed — /openapi/v1.json returned 500.
The machine-readable schema was unavailable because a value-type default broke schema export.
The document now generates.
2026-07-26
Section titled “2026-07-26”.NET SDK — IAxowlAuth.RequireSignInAsync(returnUrl).
The “this page needs a signed-in user” primitive: redirects once, guards against redirect loops,
and returns to where the user was. Previously every consumer hand-rolled this.
See .NET auth SDK.
2026-07-20
Section titled “2026-07-20”End-user MFA (SMS) for your app’s users.
An app group can require a second factor for its end users: POST /apps/{key}/auth/mfa/enroll/send
and /mfa/verify. Existing login routes are unchanged — a login that needs MFA returns
{ mfaRequired, mode, mfaToken } instead of a session, and you exchange the token after verifying.
2026-06-14
Section titled “2026-06-14”Full OIDC / OAuth 2.0 provider.
/authorize, /token, /userinfo, /end-session with Authorization Code + PKCE. A standard
OIDC client can now use Axowl as its identity provider with Authority alone — no Axowl SDK.
Implicit and hybrid flows are intentionally not supported. See OIDC.
2026-06-13
Section titled “2026-06-13”End-user tokens are RS256, verified through JWKS.
Tokens were HS256, which meant verifying one required holding an Axowl secret — that is not a
service. Tokens are now signed per organization with RS256 and published at
/api/public/orgs/{slug}/.well-known/jwks.json. No shared secret.
OIDC discovery.
/api/public/orgs/{slug}/.well-known/openid-configuration, with the token iss changed to the
per-org URL so Authority auto-wiring works. In ASP.NET Core the whole integration becomes
o.Authority = "https://…/api/public/orgs/{slug}".
Per-application CORS origins are enforced. The dashboard’s Allowed Origins field previously saved but did nothing. Browser origins you register are now honored, so cross-origin token exchange works without an Axowl code change.
Earlier changes predate this changelog. If you need the history of a specific endpoint, the reference pages cite the source file and line for every claim — that is the authoritative record.