Reference
Rate limits
Every limit Synq counts requests against, and how many it allows in what window.
Synq counts requests against these limits, per rule. A request over one answers 429 with a Retry-After header in seconds: the management API and the account API with the code rate_limited, the protocol endpoints with OAuth's too_many_requests, and the hosted pages with a page that says how long to wait. Wait that long, then try again; never retry in a loop.
| Rule | Allowed | Window | What it counts |
|---|---|---|---|
authorize.ip | 60 | 1 minute | Authorization requests from one address. |
interaction.ip | 60 | 1 minute | Sign-in pages and steps from one address. |
signin.identifier | 10 | 15 minutes | Sign-in attempts on one identifier (an email, a wallet address), from anywhere: what stops guessing one account's password from many addresses. |
reset.identifier | 5 | 1 hour | Password reset requests for one address at one brand, counted apart from sign-in tries, so asking for resets can't lock anyone's sign-in. |
federation.ip | 60 | 1 minute | Upstream providers' callbacks on the auth host from one address: each that names a flow costs a request to the provider. |
token_gate.recheck | 10 | 15 minutes | "Check again" on the token gate page for one user at one brand: each reads the chain afresh, past what was kept. |
token_gate.reads | 1200 | 1 minute | Reads of the chain for one brand's token gates, past what was kept: spent, its gates answer that they couldn't check, so one tenant's sign-ins can't spend Synq's Helius key for every other. |
token_gate.dry_runs | 30 | 1 minute | Dry runs of a token gate or a draft rule by one org (, POST /v1/orgs/{org}/token-gates/check): each may read the chain. |
token_gate.org_reads | 300 | 1 minute | Reads of the chain for one org's dry runs, past what was kept: their own budget, never a brand's sign-ins'. |
siws.ip | 30 | 1 minute | Wallet sign-in requests an app's own pages ask for, from one address: each is a row until it is spent or expires. |
token.ip | 300 | 1 minute | Token requests from one address for a public app, or an app that says nothing of itself: a confidential app's count per app instead (token.client), so a backend-for-frontend's users never share its server's address. |
token.client | 600 | 1 minute | Token requests from one confidential app, by the client_id it authenticates as. |
par.client | 600 | 1 minute | Pushed authorization requests from one confidential app (/request), as token.client. |
client_auth.ip | 30 | 1 minute | Failed app authentications from one address, at the token endpoint, /request and /authorize-challenge, checked in front of all three: what stops a caller who forges credentials from leaving the per-address limits behind. |
challenge.ip | 60 | 1 minute | Own-UI sign-in requests at /authorize-challenge from one address: a public app's caller, or the person a confidential app's server says it signs in (Synq-End-User-IP), counted for that app alone. |
challenge.client | 1200 | 1 minute | Own-UI sign-in requests from one app. |
introspection.client | 1200 | 1 minute | Introspection by one app or API. |
api_keys.client | 1200 | 1 minute | Checks of users' API keys by one API's own app (, POST /api-keys/verify). |
revocation.client | 120 | 1 minute | Revocation by one app. |
protocol.ip | 300 | 1 minute | Userinfo, introspection, revocation and sign-out from one address. |
registration.ip | 10 | 1 hour | Apps registering themselves from one address: a few an hour. |
device.poll | 1 | 5 seconds | Polls of one device code at the token endpoint: one per interval, else slow_down. |
management.ip | 600 | 1 minute | Management API requests from one address, before the caller is known: all but the calls Synq's own dashboard makes for a signed-in person, which come from one address for everyone using it. The staff API counts its own the same way, for the staff admin. |
management.principal | 600 | 1 minute | Management API requests from one API key or Synq user, whatever app they call through. |
management.connections | 10 | 1 minute | An org's own apps at the sign-in providers, set: each asks the provider whether it takes the app, so nobody tries secrets at a provider through Synq. |
custom_domain.org | 10 | 24 hours | Custom domains an org adds: each one costs a slot in Cloudflare once proven, so nobody churns hostnames through Synq. |
custom_domain.check | 1 | 1 minute | "Check now" on one custom domain: each reads its DNS and Cloudflare. |
mail.recipient | 50 | 1 hour | Email to one address, whoever sends it: nobody floods an inbox through Synq, however many orgs or brands they use (mail/limits.ts). A ceiling well above any one tenant's, so a stranger asking one brand for codes can't stop the address's email from every other. |
mail.tenant_recipient | 10 | 1 hour | Email to one address from one org or one brand: what anyone asking one tenant can use up. |
mail.ip | 30 | 1 hour | Sign-in email one client address asks for: codes to many addresses from one place. Counted by the address's /64 for IPv6. |
mail.org | 100 | 1 hour | An org's invitations. |
mail.brand | 1000 | 1 hour | A brand's sign-in email: codes, links, verification and resets. |
mail.reply_to | 5 | 24 hours | Confirmations of one brand's reply-to address: each emails an address the tenant typed, so a tenant can't mail strangers through it. |
email_domain.org | 10 | 24 hours | Sending domains one org adds: each is registered with Postmark, and a name held is a name no other org can take. |
email_domain.check | 1 | 1 minute | "Check now" on one sending domain. |
mail.sender_test | 5 | 1 hour | Tests of a brand's email one person sends themselves. |
reply_to_confirm.ip | 30 | 1 minute | The reply-to confirmation page and its button from one address. |
files.org | 60 | 1 hour | Uploads one org starts (, files/): each is a signed PUT into Synq's storage and a decode at finalize, so no tenant fills the bucket or keeps the API decoding images. |