Native flows
Native API provides three direct-issue methods. Choose Steam for a Steam game, AccessKey for an issued secret pair, or PublicKey for an Ed25519 key you control. AccessKey and PublicKey can authenticate an Account, Agent, or Automation.
| Credential | Endpoint |
|---|---|
| Steam ticket | POST /direct-issue/steam-ticket |
| AccessKey | POST /direct-issue/access-key |
| PublicKey | POST /direct-issue/public-key |
Configure all three rule layers and credentials, then have an organization OWNER take the application live. The application must be ACTIVE, with available parent organization and sector. Every direct-issue method requires a DIRECT_ISSUE ReturnRule and an applicable Layer 2 rule for the account.
Principal and credential admission
Section titled “Principal and credential admission”Layer 1 must allow the exact method below. Native derives it from the credential’s bound principal; callers cannot choose another principal kind. These rules have empty payloads.
| Principal | AccessKey | PublicKey |
|---|---|---|
| Account | ACCESS_KEY_DIRECT |
PUBLIC_KEY_DIRECT |
| Agent | AGENT_ACCESS_KEY_DIRECT |
AGENT_PUBLIC_KEY_DIRECT |
| Automation | AUTOMATION_ACCESS_KEY_DIRECT |
AUTOMATION_PUBLIC_KEY_DIRECT |
Browser polling
Section titled “Browser polling”For a confidential backend that can sign /establish, use Connect browser polling. Public clients without that backend should use Device authorization.
Steam direct-issue
Section titled “Steam direct-issue”For games shipped through Steam, Sudomimus supports a silent login that does not open a browser at all. The user does not see a login prompt; their Steam identity is exchanged directly for a Sudomimus session.
curl -X POST https://native-api.sudomimus.com/direct-issue/steam-ticket \ -H "Content-Type: application/json" \ -d '{ "applicationAnchor": "my-game", "steamTicketHex": "<hex-encoded ticket from Steamworks GetAuthTicketForWebApi>", "steamAppId": 480 }'The flow is:
- The game calls Steamworks
ISteamUser::GetAuthTicketForWebApi("sudomimus")— notGetAuthSessionTicket. The two are different ticket types and are not interchangeable. The identity string must be exactly"sudomimus"(case-sensitive); other values are rejected. - The game waits for the
GetTicketForWebApiResponse_tcallback before using the ticket. - The ticket bytes are hex-encoded and sent as
steamTicketHextoPOST /direct-issue/steam-ticket, together withapplicationAnchorandsteamAppId. - Sudomimus verifies the ticket with Steam, looks up or creates the account, and — on the happy path — returns
{ accessToken, refreshToken }in one round trip. If the application requires consent or profile data the Steam account has not provided, this returns a403with an Errand handoff instead — see When direct-issue needs consent or profile data. - The game calls
Steamworks.CancelAuthTicket(handle)after receiving the tokens.
The Steam account is the source of identity; required consent or missing profile data may still need the browser Errand.
Application configuration for Steam
Section titled “Application configuration for Steam”The application must have:
- Layer 1: a
STEAM_TICKETAuthenticationRule withallowedSteamAppIds: number[]containing this game’s Steam App ID. - Layer 2: a rule that will match — typically
STEAM_IDwithallowedSteamIds: ["*"]for any verified Steam account, or a list of specific SteamID64 strings. - Layer 3: a
DIRECT_ISSUEReturnRule.
A Steam-first account that has never linked an email needs a STEAM_ID (or ACCOUNT_ALIAS / SECTOR_SUBJECT) Layer 2 rule; an EMAIL-only Layer 2 will reject it.
This endpoint does not require a client-auth JWT — the Steam ticket itself attests both the user and the binary’s right to talk to the application.
Web counterpart. The browser-side “Sign in with Steam” button uses the
STEAM_OPENIDLayer 1 method instead ofSTEAM_TICKET. Both paths land on the same per-user Steam identity, so a user who first signed in through a game can subsequently sign in through the web button (and vice versa) without any account-linking step. See Authentication rules for theSTEAM_OPENIDrule shape.
AccessKey direct-issue
Section titled “AccessKey direct-issue”For environments without a Steam ticket but where the target Sudomimus account is already known — CLI tools, custom launchers, headless services, automated test rigs. The “proof” is a Sudomimus-issued credential pair (accessKeyIdentifier + accessKeySecret) generated from the developer portal and handed out-of-band to the operator.
curl -X POST https://native-api.sudomimus.com/direct-issue/access-key \ -H "Content-Type: application/json" \ -d '{ "applicationAnchor": "my-cli-tool", "accessKeyIdentifier": "acs_k_<uuidv4>", "accessKeySecret": "acs_t_<64-char lowercase hex>" }'Both credential strings carry mandatory prefixes:
acs_k_— the public identifier, followed by a UUIDv4.acs_t_— the secret half, followed by 64 lowercase hex characters. It is returned by the create operation and an exact retry for up to ten minutes, but never by ordinary key reads.
The prefixes are part of the canonical form. They make the two halves visually distinguishable and let secret scanners match accidentally-committed credentials by literal substring.
Application configuration for AccessKey
Section titled “Application configuration for AccessKey”The application must have:
- Layer 1: the exact AccessKey AuthenticationRule for the credential principal from the table above (empty payload). Default-deny unless explicitly added.
- Layer 2: a rule matching the target account —
EMAIL,STEAM_ID,ACCOUNT_ALIAS, orSECTOR_SUBJECT. - Layer 3: a
DIRECT_ISSUEReturnRule.
AccessKey credentials cannot create new accounts. The credential is issued against an existing Sudomimus account; if that account is deleted, every credential bound to it is rejected at login time.
Credentials are managed from with.sudomimus.com under Programmatic access → Access keys; see Manage access keys. Revocation is a soft delete (revokedAt timestamp); rotation = revoke + reissue. Expired credentials are not auto-evicted but are rejected at the handler.
This endpoint does not require a client-auth JWT either — the access-key secret is itself the credential. Embedding the client-auth private key in a distributable CLI would be reversible by any operator anyway.
PublicKey direct-issue
Section titled “PublicKey direct-issue”Register an Ed25519 public key under Programmatic access → Public keys, choosing the Account, Agent, or Automation and its application/sector coverage. Keep the private key in your own system.
- Serialize the request body, for example
{"applicationAnchor":"my-app"}. - Sign an EdDSA JWT with header
alg: "EdDSA",typ: "vnd.sudomimus.public-key-assertion+jwt", andkidequal to the registeredpky_...credential identifier. - Set
issequal tokid,audtosudomimus-native-public-key, currentiat,expat most 60 seconds later, a fresh random 128-bit base64urljti, andrequestHashto the base64url SHA-256 digest of the exact request body bytes. - POST those same bytes to
https://native-api.sudomimus.com/direct-issue/public-keywithContent-Type: application/jsonandAuthorization: SudomimusPublicKeyJWT <assertion>.
Every new attempt requires a fresh assertion and jti. The private key authenticates issuance; the resulting access token remains a bearer token. Exact request and error shapes are in the Native API reference.
When direct-issue needs consent or profile data
Section titled “When direct-issue needs consent or profile data”Direct-issue endpoints authenticate a credential in one request — they cannot pop a consent screen or ask the user to type in an email. So when an application requires a claim the user has not granted, or requires data the account does not have yet (a Steam account with no email, for instance), the call cannot just succeed. Instead it returns a 403 carrying an Errand — a short-lived browser side-trip where the user completes that work:
{ "reason": "ClaimConsentRequired", "claims": { "email": { "requirement": "REQUIRED", "state": "UNKNOWN" }, "firstName": { "requirement": "OPTIONAL", "state": "UNKNOWN" }, "lastName": { "requirement": "OFF", "state": "UNKNOWN" }, "staticAvatar": { "requirement": "SYNTHETIC_ONLY", "state": "UNKNOWN" }, "animatedAvatar": { "requirement": "OFF", "state": "UNKNOWN" } }, "errand": { "errandKey": "ernd_...", "url": "https://via.sudomimus.com/errand?key=ernd_...", "expiresAt": "2026-06-10T12:30:00Z" }}The reason is one of ClaimConsentRequired (the user must agree to share a required claim) or RequiredClaimDataMissing (consent is there, but the account data is not). Steam, AccessKey, and PublicKey direct-issue can return this handoff, but their retry credentials differ. To recover:
- Open
errand.urlin the user’s system browser. The page walks the user through any sign-in, data entry, and consent that is owed. - Poll
GET /errand/{errandKey}/status(native-api) every ~2 seconds until it reportsCOMPLETED— or just let the user tell your UI they’re done. - Retry direct-issue once with the appropriate proof: Steam must obtain a fresh ticket from
GetAuthTicketForWebApi; AccessKey may reuse its still-valid identifier and secret; PublicKey must sign a fresh assertion with a newjti. Never replay the original Steam ticket, even if it returned an Errand. Current credential, application, rule, and claim checks still apply.
curl https://native-api.sudomimus.com/errand/ernd_.../status# → { "status": "PENDING" } user still working in the browser# → { "status": "COMPLETED" } done — retry with the credential-specific proof# → { "status": "EXPIRED" } expired/consumed/unknown — re-run with the credential-specific proofA 200 from a direct-issue endpoint also carries a claims block (the same shape as in the 403), so even on success you can see which optional claims were shared and which were withheld. The full contract — the 30-minute lifetime, when an eligible retry reuses the same errandKey, and the two security tiers (consent-only vs. sign-in-required) — is in The Errand.
Tokens and Workload admission
Section titled “Tokens and Workload admission”Account access tokens use typ: vnd.sudomimus.application-access+jwt. Agent and Automation access tokens use typ: vnd.sudomimus.workload-access+jwt and add act.sub, the pairwise actor subject. In both cases sub identifies the owner account within the sector. Applications must explicitly admit the appropriate token type and enforce their own business permissions; credential scope does not grant those permissions.
Refresh tokens use vnd.sudomimus.application-refresh+jwt and contain neither owner nor actor subject. Fetch current owner profile claims from Session UserInfo. Follow Tokens and verification for signature and authority checks.