Compatibility and versioning
What may change without notice, what may not, and how you are told.
This page states what Stria promises about its interfaces, and — just as
importantly — what it does not.
Two surfaces, one promise
**/functions/v1/api-v1/* — the partner API. Promised.**
Described by openapi.yaml, validated in CI by
npm run test:openapi, and inventoried by npm run api:check so an endpoint cannot
ship undocumented.
**Everything reachable through the Supabase Data API (PostgREST) — internal.
Not promised.**
The public schema is exposed, so a workspace member holding their own user JWT can
reach tables, views and several hundred RPCs directly. That surface exists for
Stria's own client. It is unversioned, undocumented, and changes without notice in
any migration.
We are saying this plainly rather than leaving it implied, because an undocumented
surface with real users quietly acquires the obligations of a documented one. If you
build billing automation on a SECURITY DEFINER RPC you found by watching network
traffic, it will break, and the breakage will look like an outage rather than a
deprecation.
Use it for one-off scripts and exploration. Build integrations on /api-v1. If
something you need is only available internally, that is a gap worth telling us
about — the v1 surface is deliberately narrow and additive.
What counts as a breaking change
These ship only in a new major version (/api-v2), never in place:
- removing an endpoint, a field, or an enum value
- renaming anything
- narrowing a type, or making an optional request field required
- changing the meaning of an existing field
- adding a required scope to an existing endpoint
These ship without notice, so write clients that tolerate them:
- new endpoints
- new optional request parameters
- new fields in a response object
- new enum values in a field documented as extensible (
status,code) - new webhook event types
- new response headers
Concretely: parse JSON permissively, ignore unknown fields, and never assume a
response object has exactly the keys you saw during development.
Deprecation
When an endpoint is deprecated:
- It is marked
deprecated: truein the OpenAPI document and in the changelog. - It keeps working unchanged.
- Once a removal date is set, responses carry a
Sunsetheader
(RFC 8594) with that date, and a
Deprecation header.
- Minimum notice before removal is 90 days from the first
Sunsetheader.
Currently deprecated: GET /api-list-clients, superseded by
GET /api-v1/clients. No removal date set, so no Sunset header is being sent yet.
Webhook contract
Deliveries are at-least-once. Duplicates are the contract, not an edge case —
no HTTP-based delivery system can offer exactly-once, because a sender cannot
distinguish a lost request from a lost response.
Three things follow, and all three are on the receiver:
- Deduplicate on the delivery id. The
webhook-idheader (also sent as
X-Stria-Delivery-Id) is stable across every retry and across an
admin-triggered replay. A 24-hour dedupe window is enough; the retry horizon is
~31 hours but a replay of something older is deliberate.
- Do not assume ordering. There is none. Each payload carries
created_atand
enough resource state to discard a stale event — an invoice.sent arriving after
an invoice.paid for the same invoice should be dropped, not applied.
- Verify the signature over the raw body. See
webhooks.md. During a secret rotation, two signatures are valid
for the overlap window.
If a consumer outage means you missed deliveries, do not wait for a replay: poll the
matching list endpoint with updated_since. That is the supported recovery path and
it is correct regardless of what the queue did.
Rate limits
Published per plan in authentication.md. Responses carry
RateLimit-Limit and RateLimit-Policy; a 429 carries Retry-After.
RateLimit-Remaining is deliberately not sent. The limiter is a fixed-window
counter that raises on breach without reporting a remaining count, and advertising a
number we cannot compute would be worse than omitting it — clients would pace
against a value that is wrong. Back off on 429 using Retry-After with jitter.
Versioning of this policy
Changes to this document are listed in changelog.md. A change
that reduces a notice period applies only to deprecations announced after it.
More reference
- Authentication — How tokens are minted, stored, presented and revoked.
- Endpoints — Every request and response shape, with worked examples.
- Outbound webhooks — Signed HTTPS POSTs when work is signed off, a change order moves, or an invoice is paid — plus how to verify one and how to survive a duplicate.
- Rate limits — Per organization, per minute, and published rather than discovered as a random failure.
- Errors — What each status means, what the body carries, and which ones to retry.
- API changelog — Every change, dated, including the ones that were corrections.
OpenAPI specification · API access is on every paid plan · Start your 14-day trial