Establish a new authentication inquiry for an application.
const url = 'https://connect-api.sudomimus.com/establish';const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: '{"applicationAnchor":"example","authenticationConstraints":[{"method":"PASSKEY_USERNAMELESS","payload":{},"accessTokenTtlSeconds":1,"refreshTokenTtlSeconds":1}],"realizeConstraints":[{"constraintType":"EMAIL","payload":{"allowedEmails":["example"]},"accessTokenTtlSeconds":1,"refreshTokenTtlSeconds":1}],"returnMethods":[{"type":"CALLBACK","payload":{"callbackUrl":"example"}}]}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://connect-api.sudomimus.com/establish \ --header 'Content-Type: application/json' \ --data '{ "applicationAnchor": "example", "authenticationConstraints": [ { "method": "PASSKEY_USERNAMELESS", "payload": {}, "accessTokenTtlSeconds": 1, "refreshTokenTtlSeconds": 1 } ], "realizeConstraints": [ { "constraintType": "EMAIL", "payload": { "allowedEmails": [ "example" ] }, "accessTokenTtlSeconds": 1, "refreshTokenTtlSeconds": 1 } ], "returnMethods": [ { "type": "CALLBACK", "payload": { "callbackUrl": "example" } } ] }'Creates a new authentication inquiry under the calling application.
The request MUST carry a client-auth JWT in the Authorization header
using the SudomimusClientJWT scheme. The JWT binds the request body
bytes via a body_sha256 claim and carries a single-use jti to
prevent replay. See the SudomimusClientJWT security scheme below
for the full claim contract.
All three narrowing fields (authenticationConstraints,
realizeConstraints, returnMethods) are optional. When absent the
inquiry imposes no per-inquiry narrowing on that layer. When present
the array MUST be non-empty — an empty array is explicitly rejected
with HTTP 400.
Authorizations
Section titled “Authorizations ”Request Body required
Section titled “Request Body required ”object
Public anchor identifying the integrating application.
Optional per-inquiry narrowing of the application’s authentication-rule layer. Absent means no narrowing. If present, the array MUST be non-empty; empty arrays are rejected with 400.
object
Which authentication method this constraint narrows to.
Empty payload — narrows to usernameless (discoverable-credential) passkey login, the “Sign in with a passkey” entry shown before any email is entered. Realize authorization is still decided by Layer 2.
object
Empty payload — narrows to email-first (“reasoned”) passkey login, the passkey option offered after the user enters their email. Realize authorization is still decided by Layer 2.
object
Empty payload — email-verification constraints carry no further parameters.
object
Steam-native-ticket payload. Gates native-api’s
/direct-issue/steam-ticket flow. allowedSteamAppIds is the
non-empty list of Steam App IDs whose tickets are accepted.
object
Steam OpenID 2.0 carries no app-id concept (that is native-ticket-specific). Any Steam account is accepted as long as the application’s rule set allows STEAM_OPENID at all.
object
Gates native-api’s /direct-issue/access-key flow. Credentials
live in the dedicated AccessKeyCredential table; no row of
this method ever appears in the Authentication table.
object
Phase 1: any Google account is accepted as long as the
application’s rule set allows GOOGLE_OAUTH at all. A later
phase will add allowedHostedDomains: string[] for Google
Workspace gating.
object
Empty allowedGitHubOrgs array means no org gating — any
GitHub account is accepted. Non-empty means the user must be a
member of at least one listed organization (case-insensitive
match on the org login). The read:org OAuth scope is only
requested when at least one matching rule carries a non-empty
allowlist; applications without org gating keep the minimal
read:user user:email consent screen.
object
Phase 1: any Discord account is accepted as long as the
application’s rule set allows DISCORD_OAUTH at all. Phase 1.5
will add allowedDiscordGuilds: string[] for guild (server)
gating, which will additionally request the guilds OAuth
scope and fetch GET /users/@me/guilds.
object
Battle.net (Blizzard) has no per-application gating concept, so any Battle.net account is accepted as long as the application’s rule set allows BATTLENET_OAUTH at all. Empty payload. Battle.net is an email-less provider, so Layer-2 EMAIL rules fail closed against a Battle.net-only account.
object
X (formerly Twitter) has no per-application gating concept, so any X account is accepted as long as the application’s rule set allows X_OAUTH at all. Empty payload. X is an email-less provider, so Layer-2 EMAIL rules fail closed against an X-only account.
object
Per-constraint override for access token lifetime. Resolved at realize time.
Per-constraint override for refresh token lifetime. Resolved at realize time.
Optional per-inquiry narrowing of the application’s realize-rule layer. Absent means no narrowing. If present, the array MUST be non-empty; empty arrays are rejected with 400.
object
Which realize-rule kind this constraint narrows to.
object
List of email addresses or glob patterns the realized identity must match. Glob patterns are bounded by server-side limits to prevent regex backtracking attacks.
Per-realize-time check against the realized account’s SteamID64.
Each entry is either the literal "*" (wildcard, allow any
Steam identity) or a decimal SteamID64 string.
object
Exact match on the realizing account’s account alias — the user-visible, application-invisible, rotatable handle the user reads in the With portal and shares out-of-band with whoever configures the rule. No wildcard — because the account does not yet exist during fresh registration, this constraint matches nothing for new sign-ups. The alias is opaque: it is compared by exact-string equality and never parsed or format-validated.
object
Exact match on the realizing account’s sector subject for the
realizing application’s sector — the application-visible token
sub the owner already sees in their own logs. No wildcard.
Rotating the subject locks the user out (it becomes a brand-new,
not-yet-allow-listed identity) rather than letting them bypass the
rule. The subject is opaque: compared by exact-string equality and
never parsed or format-validated.
object
Per-constraint override for access token lifetime. Resolved at realize time.
Per-constraint override for refresh token lifetime. Resolved at realize time.
Optional per-inquiry return-method declaration. Doubles as the concrete delivery info for CALLBACK (carries the callback URL). Absent means no per-inquiry narrowing (CALLBACK is unreachable for this inquiry because no URL is anchored). If present, the array MUST be non-empty; empty arrays are rejected with 400.
object
object
Concrete callback URL for this inquiry. The host MUST match one of the application’s allowed callback domains.
object
Empty payload — STATUS_POLL carries no per-inquiry parameters.
object
object
Empty payload. Tokens are surfaced directly in the UI at
realize time and the inquiry is marked redeemed immediately;
any subsequent /redeem for the same inquiry fails with
InquiryAlreadyRedeemed.
object
object
Empty payload. Opts the application in to native-api’s
direct-issue flows (/direct-issue/steam-ticket,
/direct-issue/access-key). Per-inquiry payload is empty
because direct-issue does not flow through /establish.
object
object
Accepted on the wire for symmetry, but in practice OIDC is not declared per-inquiry — the OIDC API drives its own inquiry against Connect via CALLBACK-to-self. The matching per-inquiry payload is therefore empty.
object
Responses
Section titled “ Responses ”Inquiry established.
object
Public half of the inquiry key pair; safe to share with the user agent.
Private half of the inquiry key pair; must stay on the originating client.
Example generated
{ "applicationAnchor": "example", "exposureKey": "example", "hiddenKey": "example"}Client-auth JWT missing, malformed, expired, or invalid.
Error response body. The Connect service emits { "reason": "<SymbolDescription>" }
for known failure modes. When the reason symbol’s description begins with
PRIVATE, the body is empty (zero bytes) and only the HTTP status carries
signal — both reason and the body itself are absent in that case.
object
Stable machine-readable reason code.
Example generated
{ "reason": "example"}Application is disabled.
Reason ApplicationDisabled.
Error response body. The Connect service emits { "reason": "<SymbolDescription>" }
for known failure modes. When the reason symbol’s description begins with
PRIVATE, the body is empty (zero bytes) and only the HTTP status carries
signal — both reason and the body itself are absent in that case.
object
Stable machine-readable reason code.
Example generated
{ "reason": "example"}default
Section titled “default ”Error response.
Error response body. The Connect service emits { "reason": "<SymbolDescription>" }
for known failure modes. When the reason symbol’s description begins with
PRIVATE, the body is empty (zero bytes) and only the HTTP status carries
signal — both reason and the body itself are absent in that case.
object
Stable machine-readable reason code.
Example generated
{ "reason": "example"}