OAuth
The OAuth item is available for Website integration and Plugin applications. It contains the OAuth client data, allowed return addresses, and access policies. A Ready-made solution does not have this item.
The application owner, administrator, and developer can change OAuth settings and work with the Client Secret. A viewer can see the Client ID and other settings in read-only mode; the Client Secret is not shown to them.
Client ID and Client Secret
The OAuth credentials section contains:
- Client ID, the public application identifier; use Copy Client ID to transfer it;
- Client Secret, the secret used to authenticate the application server at the token endpoint. Keep it only on the server and never place it in browser code, logs, or a public repository. You can show or hide and copy the value.

Client Secret regeneration
Regenerate secret opens a confirmation dialog. Confirmation issues a new Client Secret and immediately prevents the old one from exchanging a code or refreshing tokens. Cancel makes no changes.

Store the new value and replace the secret on the integration server. Existing access tokens continue to work until they expire or the authorization is revoked; regenerating the secret does not revoke them by itself.
Redirect URI
In Redirect URIs, select Add URI and enter the return address. You can remove an address that is no longer needed.

An address without a protocol is stored with https://. Explicit http:// is allowed only for loopback addresses: localhost, its subdomains, 127.x.x.x, and ::1. An address with credentials, a #... fragment, or an unsafe protocol is rejected.
The redirect_uri in the authorization request must exactly match a stored value. The same address must be passed again during code exchange. Scheme, host, port, path, query parameters, and trailing slash are significant.
Two access scenarios
A Website integration configures two scenarios independently in OAuth access scenarios:
- the Project access tab opens the project policy. The authorization belongs to one project, and the API checks the permissions granted in that project;

- the User access tab opens the user policy. The application acts on the user's behalf, while every call remains limited by both the approved permissions and the user's current access to the target resource.

The scenario is selected by each authorization request through subject, not when the application is created. For subject=project, project_id is optional: when it is absent, Senler AI asks the user to select an available project. For subject=user, do not pass project_id. When subject is absent, project is used. A Plugin supports only the project scenario.
Permissions
Project and user policies each have their own permissions. Two modes are available for each policy:
- Selected permissions requests the entire selected set when
scopeis absent. Thescopemay contain a subset. The user must be able to grant every requested permission; - Available to the user makes Senler AI exclude permissions the authorizing user cannot grant. Pass
scope=project_accessorscope=user_accessfor the corresponding scenario, or omitscope.
In the second mode, Request all available permissions sets the upper boundary. When disabled, the application receives the intersection of selected and available permissions; when enabled, it receives every permission allowed for that OAuth scenario that the user can grant. The exact set is always shown before consent.
For project authorization, can_view_projects is added automatically and cannot be removed. User authorization can include project, application, and account permissions, but it cannot manage API tokens, Client Secrets, or application deletion.
Narrowing a policy immediately narrows active authorizations; a user authorization with no remaining permissions is revoked. Expanding a policy does not add permissions to previously issued tokens—the user must complete OAuth again. Select all and Deselect all change optional permissions.

Save applies both policies and the Redirect URIs.

Authorization request
Open https://senler.io/oauth/authorize in the browser with these parameters:
response_type=code, the only supported response type;client_id, the application's Client ID;redirect_uri, an exact registered address;subject=project|user, the access scenario;projectis used when omitted;project_id, an optional preselected project only forsubject=project;scope, an optional set ofcan_*permissions in selected mode orproject_access/user_accessin dynamic mode;state, a random one-time value stored by the integration until the user returns.
Example project authorization with project selection in Senler AI:
https://senler.io/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=https%3A%2F%2Fexample.com%2Foauth%2Fcallback&subject=project&scope=project_access&state=RANDOM_STATE
For the user scenario, replace subject=project with subject=user, use the matching scope, and do not add project_id.
After approval, Senler AI returns code and the original state to the Redirect URI. After denial, it returns error=access_denied, error_description, and state. The integration server must compare the returned state with the stored value. The code is valid for 10 minutes and can be used only once.
Exchanging the code for tokens
Send POST https://api.senler.io/api/apps/oauth/token as application/x-www-form-urlencoded or JSON. Client ID and Client Secret can be passed through HTTP Basic or body fields, but not as two conflicting values.
curl -X POST https://api.senler.io/api/apps/oauth/token \
-u 'CLIENT_ID:CLIENT_SECRET' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'code=AUTHORIZATION_CODE' \
--data-urlencode 'redirect_uri=https://example.com/oauth/callback'
The response contains access_token, refresh_token, token_type=Bearer, expires_in, scope, and subject. It also returns project_id for subject=project or user_id for subject=user. Use the access token as Authorization: Bearer ... and rely on expires_in instead of assuming a lifetime.
Refreshing tokens
To refresh, send grant_type=refresh_token and the current refresh_token to the same endpoint, authenticating the OAuth client again.
curl -X POST https://api.senler.io/api/apps/oauth/token \
-u 'CLIENT_ID:CLIENT_SECRET' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token=REFRESH_TOKEN'
After a successful request, the previous refresh token is revoked and the response contains a new token pair. Store both new values before the next refresh.
Revoking a user authorization
To disconnect one external client or revoke your own user authorization, open Connected applications in Account settings. Disconnecting a client ends access only for that connection; revoking the user authorization disconnects all of its clients. Client Secret regeneration replaces the OAuth client key, but it does not revoke user access that has already been granted.
Token endpoint errors
The token endpoint returns { "error": "...", "error_description": "..." }:
invalid_requestmeans a required parameter is absent or parameters conflict;invalid_clientmeans Client ID or Client Secret is invalid; the HTTP status is 401;invalid_grantmeans the code or refresh token expired, was already used, was revoked, or was issued to another client;unauthorized_clientmeans the application type does not support OAuth;unsupported_grant_typemeansgrant_typeis not supported.
Start a new authorization after invalid_grant, verify the current Client Secret after invalid_client, and correct other errors using error_description. Do not retry the same request indefinitely.