Skip to content
AXOWLDevelopers
Quickstart

Self SSO domains

Axowl has two kinds of SSO, configured and billed separately:

Self SSOExternal SSO
Who handles sign-inAxowl (passkey, magic link)Your IdP (Okta, Entra ID, Google Workspace, SAML/OIDC)
What you registerVerified domains (this page)IdP connections — IdP connections
BillingPer verified domain beyond the plan allowance — $10 / domain / monthPer connection beyond the plan allowance
DashboardOrganization settings → User management (left) · App group → Login methods → SSOOrganization settings → User management (right)
kindStored asWhat it does
emailCompany email domain (for example acme.com)“Sign in with SSO” on Axowl finds your organization from a work email. If an external IdP connection routes that domain, the person goes to that IdP; otherwise they continue with their Axowl account. External IdP connections can only route domains verified here.
appApp domain of one app group (for example shop.acme.io)Apps on different domains share one sign-in (the hosted portal hands the session over to the app’s callback origin). A verified domain also covers its subdomains.

Personal and disposable email domains (gmail.com and the like) are refused for email. A domain verified by another organization can’t be registered or verified again — 409 domain_claimed.

Org-scoped (/api/org/{slug}/sso-domains, session-authenticated). Writes need the org.idp_connection.update permission (the same one as IdP connections).

Method · RoutePurpose
GET ?kind=email|app&app_group_id=List domains (both kinds when kind is omitted) and a billing summary: included (-1 = unlimited), verified, billable, unit_price_usd, monthly_usd.
POST “Register a domain. Body: domain, kind (email or app), app_group_id (required for app). Returns the row with the TXT record to publish.
POST /{id}/verifyLook up the TXT record now. Returns verified and, when not verified, reason (txt_not_found, txt_mismatch, dns_lookup_failed) plus up to five found_records. 404 domain_not_found for an unknown id; 409 domain_claimed if another organization verified the domain first.
DELETE /{id}Remove a domain. An email domain still routed by an active external IdP connection returns 409 domain_bound_to_connection — turn that connection off first.
POST /app-modeTurn an app group’s app domains on or off without deleting them. Body: app_group_id, integrated (bool). Off domains keep their verification but are not used for sign-in sharing and are not billed. Returns integrated and the number of domains changed.

Each row: id, domain, kind, app_group_id, verified, verified_at, active (false = turned off with /app-mode), txt_name, txt_value, last_checked_at, external_connection_type (email domains routed to an external IdP).

Publish a TXT record, then call verify:

TypeNameValue
TXT_axowl-challenge.acme.com (txt_name)the txt_value from the row
Terminal window
curl -X POST "https://api.axowl.com/api/org/acme/sso-domains" \
-H "Content-Type: application/json" --cookie "$AXOWL_SESSION" \
-d '{"domain":"acme.com","kind":"email"}'
# after the TXT record is live
curl -X POST "https://api.axowl.com/api/org/acme/sso-domains/$DOMAIN_ID/verify" --cookie "$AXOWL_SESSION"

DNS changes can take a few minutes to show up; call verify again until verified is true.

If your organization already verified the same domain for the other kind (for example acme.com as an email domain, then again as an app domain), the new row is verified right away — no second TXT record. A TXT value issued for either row of the same domain in the same organization also verifies the other.

/api/org/{slug}/sso-login-policy — how invited employees enter the organization.

MethodPurpose
GETAny member. Returns staff_invite_sso_only, staff_login_basis, has_enterprise_idp, has_email_domain.
PUTNeeds org.idp_connection.update. Body: staff_invite_sso_only (bool), staff_login_basis (idp or idp_or_domain). Returns the saved values and enforced — false while the organization has neither a company IdP nor a verified company email domain. 400 invalid_basis for any other basis.
  • staff_invite_sso_only — applies to employee invitations (any role except Guest, Freelancer, outside director, project guest and meeting guest):
    • Company IdP connected: the invitation is accepted only after the invitee signs in with the company IdP and the IdP’s email matches the invited address. Both accept routes return 400 sso_required until then. Inviting a personal email address (Gmail and the like) to an employee role is refused up front.
    • Only a verified company email domain (no IdP): the invited address must be on that domain (subdomains count). Other addresses are refused when the invitation is sent, and both accept routes return 400 staff_domain_required. Opening the invitation link from the email proves the mailbox.
    • Neither: not applied yet.
  • staff_login_basis — when the organization sign-in page (/o/{slug}/login) first asks “Registered employee or Guest”: idp only when a company IdP is connected; idp_or_domain (default) also when a company email domain is verified. Guests sign in with their own Axowl account and still need an invitation to enter the organization.

POST /api/public/sso/discover — public, rate-limited. Body: { "email": "[email protected]" }.

ResponseMeaning
{ "found": false }No organization has verified this domain (also returned for malformed or personal addresses).
{ "found": true, "mode": "external", "org_name", "redirect_url" }Send the browser to redirect_url — the organization’s IdP sign-in.
{ "found": true, "mode": "axowl", "org_name", "org_slug" }Continue with the Axowl account (passkey or magic link).

POST /api/public/orgs/{slug}/staff-check — public, rate-limited. Body: { "email": "[email protected]" }. Used by the organization sign-in page (/o/{slug}/login) when an employee picks “Registered employee” and the organization has a verified company email domain but no external IdP.

ResponseMeaning
{ "registered": true }The address is on a domain this organization verified and belongs to an active (or pending) member.
{ "registered": false }Anything else — not a member, suspended or left, an unverified or personal domain, or an unknown organization. No reason is given.

Verified email domains and app domains are added together and compared with the plan allowance: Basic 0 · Pro 1 · Pro+ 3 · Business 10 · Enterprise unlimited. Each verified domain beyond that is $10 per month, charged with the monthly subscription and shown on the invoice under SSO. Domains waiting for DNS are not counted. Adding domains requires the Pro plan or higher — 403 UPGRADE_REQUIRED (feature: "sso").

Status · codeWhen
400 invalid_kindkind is not email or app
400 invalid_domainNot a domain name
400 personal_email_domainPersonal or disposable email domain for kind: "email"
400 app_group_required · 404 app_group_not_foundkind: "app" without an app group of this organization
409 domain_claimedAnother organization already verified this domain
409 domain_existsAlready registered here
404 domain_not_foundVerify or delete with an id that is not a domain of this organization
409 domain_bound_to_connectionDelete refused while an external IdP connection routes the domain
403 UPGRADE_REQUIREDPlan below Pro