The SAML SSO round-trip
These four operations are the service-provider side of SAML 2.0 SSO — the route an organization’s IdP administrators walk to wire Okta, Entra ID, or similar into Orbit’s sign-in. The page’s{orgSlug} path segment selects the SAML configuration; it is a URL-safe organization slug, not an API-key credential, and the operations are deliberately unauthenticated because they begin a session rather than require one.
Sequence the round-trip like this. (1) GET /auth/saml/{orgSlug}/metadata returns the service-provider XML the IdP admin imports to register Orbit: entity ID, ACS URL, and signing details. (2) GET /auth/saml/{orgSlug}/login?redirect=/settings/team issues the SP-initiated AuthnRequest and 302-redirects the browser to the IdP’s SSO URL; any redirect value is signed into RelayState and sweeps the post-auth destination back. (3) The IdP POSTs the signed SAMLResponse to /auth/saml/{orgSlug}/callback — the Assertion Consumer Service verifies signature, audience, and the replay window, provisions the user on first login, then 302-redirects to the dashboard sign-in ticket that mints the session cookie. (4) GET /auth/saml/{orgSlug}/logout?nameId=<subject> builds a LogoutRequest for the IdP’s SLO endpoint when single logout is configured; otherwise it answers 200 with a logged_out status, and the local ceremony destroys the session.
A worked check-in sequence in cURL:
cURL
The four calls above do not return the normal JSON envelope — skip the
read-ahead envelope check for them. Success on this page is a 302
redirect (
login, logout, and the IdP-driven callback) or a
Content-Type: application/xml metadata download (metadata), not a
{ "data": … } body. Only failures come back in the standard
{ "error": …, "meta": { "request_id" } } envelope (see the
error shapes table) — or, when single logout is not
configured on the org, logout answers 200 with
{ "data": { "status": "logged_out" } }, the one non-error JSON body on
this page.Same round-trip from an SDK escape hatch
The SAML ceremonies are browser-first, so no SDK ships a typed helper for them — the Node, Python, and Go SDKs all reach the same four routes through their generic request escape hatch (orbit.request, client.request,
client.Request). You keep the SDK’s auth, retries, and base URL against
these raw paths. The practical server-side uses are the two machine-readable
steps — fetch the SP metadata XML to ship to the IdP admin, and probe
the login redirect in a smoke check (the client must not auto-follow the
302 because it targets the IdP, not Orbit). The callback remains the IdP’s
own POST; there is nothing to call from your side.
Error shapes
Failures land in the standard{ "error": …, "meta": { "request_id", "timestamp" } } envelope — meta.request_id is the one handle support
needs. The request ids pinned below (req_auth_saml_login,
req_auth_saml_logout, req_auth_saml_callback, req_auth_saml_metadata)
are worked examples of that handle, one per operation. The envelope’s
error.code is the machine-readable discriminator; the callback
rejections also carry a details.reason for the exact check that failed.
Plan for these on the round-trip:
ERR_BAD_RELAY_STATE, AUTH_METADATA_MISMATCH, and CHALLENGE_TERMINAL
are the three rejection classes the onboarding round-trip hits most —
RelayState forgery/staleness on the ACS POST, IdP-side metadata drift, and
a second submit of a single-use ceremony. All three recover the same way:
never retry the same payload, restart GET …/login.