Connected apps (OAuth 2.0)
A connected app is an integration that authenticates with OAuth 2.0 instead
of a fixed API key. It trades its client id and secret for an
access token that lasts one hour, and the REST API accepts that
token wherever it accepts a key. Each company is its own OAuth server: the
issuer is your company address, e.g. https://acme.mcamgr.com, with metadata at
/.well-known/oauth-authorization-server.
Manage apps under Settings → Connected apps: the same permissions as API keys (edit company settings to see and revoke; API access as well to add apps and issue secrets), and an app can never be given more than the person adding it could do on screen.
Two kinds of app
| Kind | For | Flow |
|---|---|---|
| Service | A back-office job that acts as itself — a nightly sync, an accounting export. | Client credentials |
| Acts for a user | A tool your staff sign in to, which works as them. | Authorization code + PKCE |
Service apps: client credentials
curl -u mca_client_…:mca_cs_… -d grant_type=client_credentials \
https://acme.mcamgr.com/oauth/token
→ { "access_token": "mca_at_…", "token_type": "Bearer", "expires_in": 3600, "scope": "deals:read payments:read" }
The client may also send client_id and client_secret in the form body.
scope= (space-separated) asks for fewer scopes than the app has; leave it out
for all of them. Reuse a token until it expires — an app asking for a new one
on every call is throttled.
Apps that act for a user: authorization code + PKCE
-
Register the app's return addresses (redirect URIs). They must be https (http only for
localhost) and are matched exactly. -
Send the person to
https://acme.mcamgr.com/oauth/authorize?response_type=code&client_id=mca_client_… &redirect_uri=https://app.example.com/callback&scope=deals:write%20payments:read &state=<random>&code_challenge=<BASE64URL(SHA256(verifier))>&code_challenge_method=S256They sign in if needed and see what the app asks for. Anything beyond their own permissions is shown and left out (a write they lack becomes read when they hold it). Allow returns them to your address with
?code=…&state=…; Deny with?error=access_denied. -
Within 60 seconds, exchange the code:
curl -u mca_client_…:mca_cs_… https://acme.mcamgr.com/oauth/token \ -d grant_type=authorization_code -d code=… -d code_verifier=… \ -d redirect_uri=https://app.example.com/callback → { "access_token": "mca_at_…", "refresh_token": "mca_rt_…", "expires_in": 3600, "scope": "deals:write" } -
When the access token expires,
grant_type=refresh_token&refresh_token=mca_rt_…returns a new pair. Refresh tokens rotate: use the new one next time. A refresh token or code used twice is treated as stolen and every token of that authorization is revoked. Refresh tokens last 30 days.
Every call made with such a token is checked against the person as they are now: lose a permission and the app loses it too; be deactivated and its tokens stop working. Funding, payment and refund rules apply exactly as for that person on screen, and the audit trail names them.
Secrets, revoking, the trail
- The client secret is shown once. New secret issues another and keeps the old one working for 24 hours.
- Revoke stops the app and every token it holds, at once.
- For an app that acts for users, the list shows who has authorized it. Anyone can withdraw their own authorization; edit company settings can withdraw anyone's.
- Narrowing an app's scopes applies to tokens already issued.
POST /oauth/revoke(RFC 7009) lets the app revoke its own token; revoking a refresh token takes its access tokens with it.POST /oauth/introspect(RFC 7662) tells it whether one of its tokens is still live.- Every app change, token issued and token revoked is in the audit log, and API calls appear in the request log like a key's.
Can’t find what you need? Return to the app or contact your administrator.