Skip to content
Anlyon
Esc
↑↓navigate↵open⌘Jpreview
On this page

Changelog

Every change to the Anlyon API contract, with the date it took effect and a migration note.

Every change to the API contract is listed here with a migration note. The newest entry is at the top. Dates are UTC.

2026-10-05

A plan counts environments, and every workspace starts with Production

Breaking, billing responses. limits.maxEnvironments and betaAllowances.environments are one number, or the string "unlimited". They were objects keyed by development, staging and production.

Affected responses:

  • GET /api/v2/billing/plan
  • GET /api/v2/billing/plans
  • GET /api/v2/public/pricing-policy
Plan Environments
Early Beta Access 3
Free 1
Production 2
Growth 3
Enterprise unlimited

What else changed with it:

  • A new workspace gets a default environment of kind production, named Production, with protection on. Existing environments keep their kind, name and slug.
  • A workspace creates environments of kind development, staging and production inside one total. PATCH /api/v2/workspaces/{workspaceId}/environments/{environmentId} changes kind with no plan check.
  • A create over the total returns 429 QUOTA_EXCEEDED.
  • Early Beta Access includes environment protection, promotion between environments and the email notification channel. Email is capped at 500 emails per workspace per UTC month. features.protectedProduction and features.controlledPromotion are true there.
  • The message of PAID_PLANS_UNAVAILABLE reads “Paid plans arrive after the beta. Nothing was charged. Your workspace keeps its Early Beta allowances.” The message of HTTPS_REQUIRED reads “HTTPS is required.” Codes and statuses are unchanged.

Migration note.

  • Read maxEnvironments and betaAllowances.environments as a number or the string "unlimited". A client that read maxEnvironments.production, or summed the three keys, reads the one value.
  • @anlyonhq/sdk types BillingPlanLimits.maxEnvironments and BetaAllowances.environments as number | 'unlimited'. Update the package and fix the type errors it reports.
  • Deleting a secret or a schedule in a protected environment needs confirmProtected: true. That now applies to the default environment of a new workspace.
  • A client that matched on the old message text of PAID_PLANS_UNAVAILABLE or HTTPS_REQUIRED matches on the code.

The Stripe refund adapter accepts a live or a test key

The stripe.refund adapter config names mode: "live" or mode: "test". The credential must be a Stripe key of that mode, and the payment intent must belong to that mode. A mismatch is refused before anything is sent. The fault drill runs with mode: "test".

Migration note. An action created with mode: "test" keeps working with its test key. To refund live payments, create an action with mode: "live" and store a live key as its secret.

2026-10-03

Notification routes are split into read, write and manage

Security fix, breaking. Every method on /api/v2/notifications used to need notifications:read. A key with that scope could change the alert webhook, the email recipients and send a test webhook.

Route Scope Role
GET /, GET /unread-count, GET /preferences notifications:read any member
GET /preferences/me (new) notifications:read, needs a user any member
PATCH /preferences/me (new) notifications:write, needs a user any member
PATCH /{id}/read, PATCH /read-all, DELETE /{id} notifications:write any member
PATCH /preferences, POST /preferences/test-webhook notifications:manage admin
  • notifications:write is in the default API key scope set.
  • notifications:manage is privileged. No key receives it by default.
  • A member mutes notification types for themselves with /preferences/me. A muted type is still raised and still delivered to every channel and every other member.
  • An API key has no user and answers 403 USER_IDENTITY_REQUIRED on /preferences/me.

Migration note.

  • An OAuth client connected before 2026-10-03 must re-consent before it can mark notifications read, dismiss them or change notification settings.
  • An API key with an explicit scope list needs notifications:write added to mark read or dismiss, and notifications:manage to change workspace notification settings. Until then those calls answer 403.

A third adapter, resend.email_send

Additive. POST /api/v2/actions and PATCH /api/v2/actions/{ref} accept adapter.type: resend.email_send with the config { domain, fromAddresses? }.

  • Impact is counted in emails, one per recipient across to, cc and bcc.
  • succeeded means Resend accepted the send and returned an email id. A read-back that matches grades the effect confirmed. Neither grade means the email was delivered.
  • A replay is offered for 24 hours, the time Resend keeps an idempotency key.

Migration note. None. Existing actions are unchanged.

Any action can be governed, and every effect returns a grade

An action may carry impact, verify or governed: true. Such an action is governed: each invocation creates an effect, binds its approval to the exact request, reserves its declared quantity against every applicable impact limit before dispatch, and returns a receipt. An action with no declaration and no adapter is unchanged.

Additions:

  • Action and ActionVersion return governed, impact and verify.
  • Effect returns grade: confirmed, acknowledged, unknown, failed, refused, denied or pending. GET /api/v2/actions/effects accepts grade as a filter.
  • ImpactLimit returns unit, and POST /api/v2/impact-limits accepts a declared unit as dimension.
  • Changing or removing what a governed action declares needs the actions:govern scope.

Breaking for typed clients. dimension on ImpactQuantity, ImpactLimit and CreateImpactLimitRequest is no longer a closed enum of money and resource_mutations. A workspace that declares a unit, such as emails, sees that unit name there.

Migration note. Treat dimension as a string. A workspace that declares no unit sees the same two values as before. Handle an unrecognised grade value as you would unknown.

Was this page helpful?