Endpoints
For the Accounts profile and community API, use
resource=https://accountapi.sonoransoftware.com throughout authorization, token exchange, and refresh.
Existing Sonoran MCP integrations continue to use their own resource and product permissions.
Scopes
Request only what your integration needs. Scopes are separated by spaces and must be included in the client’s registration.
Existing resource-specific scopes
cad.admin.read, cad.admin.write, cms.read, and cms.write remain available
for their corresponding Sonoran integrations. They do not grant subscription access in this API.
Community lookup does not grant permission to administer a community or modify its subscription.
1. Register your application
Register once per application/environment and store the returnedclient_id. Supply one to ten complete
callback URLs. Production web callbacks must use HTTPS. Credentials and URL fragments are rejected.
Use an HTTPS callback on your application’s domain, as shown below. Native desktop clients may also use an HTTP IP loopback /callback URL.
201 response includes client_id, redirect_uris, and the allowed scope string.
The optional client_uri is your application’s HTTPS website URL. Set logo_uri to a publicly accessible
HTTPS image URL to display your application’s logo on the consent page. A square image works best;
without a logo, Sonoran displays a generic application icon. Supplying a logo does not verify your application.
Registration does not grant access to an account. Users must approve an authorization request.
To add scopes to an existing integration, register a new client with those scopes; existing registrations
and tokens are not automatically expanded.
2. Redirect the user to Sonoran
For each login, generate a cryptographically randomstate, nonce, and PKCE code_verifier.
The verifier must be 43–128 characters using letters, digits, -, ., _, or ~.
Set code_challenge to the base64url-encoded SHA-256 hash of that verifier, without padding.
Keep the verifier and expected state/nonce tied to the user’s login attempt.
Open the authorization endpoint with these URL-encoded query parameters:
redirect_uri must exactly match a URL registered
for this client. Only the listener port may vary for supported native IP loopback callbacks.
Sonoran checks verification for the callback actually used in this request.
3. Validate the callback and exchange the code
After approval, the callback receivescode, the original state, and
iss=https://accountapi.sonoransoftware.com. Validate state against the login attempt and validate iss
before exchanging the code. If the user cancels, handle error=access_denied.
access_token, token_type: "Bearer",
expires_in: 900, granted scope, and resource. openid adds an id_token;
offline_access adds a refresh_token.
Validate ID tokens with the published signing keys, issuer, your client ID as audience, expiry, and original
nonce. Use the stable sub as your user identifier. Use access tokens for API calls; never use an ID token
as a bearer API token or put tokens in query strings.
4. Read the approved information
openid profile:
*.communities.read scopes.
Use ?product=cad, ?product=cms, or ?product=radio to request one product;
requesting a product without its scope returns 403.
idis the product’s internal community identifier; treat it as an opaque string. Radio IDs may be numeric strings.public_idis the community’s public ID. Use the product plus internalidas the integration key.ownerindicates registered ownership.statusisownerormember; it is a relationship label, not an administrator permission or approval/banning status.subscriptionis omitted entirely without that product’s*.subscriptions.readscope.tieris the product-reported version (for exampleFREE,PLUS,PRO, or a Sonoran One version); handle additional values andUNKNOWN.activereports the product’s subscription status.- An empty array means the selected products report no communities. Provider lookup failures return
503(temporarily_unavailable), so do not treat a failed request as loss of membership or a downgrade.
Refresh and disconnect
If you requestedoffline_access, refresh with form fields grant_type=refresh_token, client_id,
refresh_token, and the same resource at /oauth2/token. Refresh tokens expire after 30 days.
Each refresh returns a replacement token: store it atomically and never reuse the old one.
Reusing an old refresh token revokes that token family.
To disconnect, POST form fields client_id and token=YOUR_REFRESH_TOKEN to /oauth2/revoke.
This revokes its refresh-token family. Already-issued access tokens expire after 15 minutes.
Delete your locally stored tokens when the user disconnects.

