Contents
OAuth & OpenID Connect API
Authorization server for apps and AI assistants that act on behalf of a Nucleo user.
Nucleo Core is the identity provider of the platform: every person signs in once at
auth.nucleoplatform.com and every module (Brain, Catalog, Commerce, Intelligence) trusts the
access tokens it issues.
External clients — MCP connectors in Claude or ChatGPT, partner tools, local scripts — use the
standard OAuth 2.0 authorization code flow with PKCE (S256). They can discover the endpoints
from the metadata documents (RFC 8414 / OpenID Connect Discovery) and register themselves with
Dynamic Client Registration (RFC 7591), without asking Nucleo for credentials. With the scope
openid the code exchange also returns a signed ID token (OpenID Connect Core).
Every token belongs to a person: there is no server-to-server (client_credentials) grant. A
partner integration acts on behalf of the user who connected it.
Tokens issued to self-registered clients always carry the scope nucleo.ai_tools: they are valid
for the Nucleo AI tools (the MCP connector and the modules' /api/v1/ai-tools), for userinfo
and for revoking themselves — every other Nucleo API answers 403 insufficient_scope.
The user always sees a consent screen before such a client receives a token.
Authentication
nucleoOAuthOAuth 2.0 · authorizationCodeAuthorization code with PKCE (S256). Register your client at
/oauth/register(or ask Nucleo for a pre-registered one). Self-registered clients' tokens always carrynucleo.ai_tools.authorize: https://auth.nucleoplatform.com/oauth/authorizetoken: https://auth.nucleoplatform.com/oauth/tokenscopes: openid, profile, email, offline_access, nucleo.ai_toolsbearerTokenHTTP bearer (JWT)Authorization: Bearer <access_token>obtained from/oauth/token.
- Base URL
- https://auth.nucleoplatform.comProduction issuer (`iss`). All endpoints below are relative to it.
- Who calls it
- AI assistants, Partners
- Endpoints
- 10
- OpenAPI 3.1 specification
- oauth.yaml
Discovery
Machine-readable metadata. Start here: a compliant client needs nothing else than the issuer URL
https://auth.nucleoplatform.com.
/.well-known/openid-configurationGet OpenID Connect discovery document
OpenID Connect Discovery 1.0 document. Public, cacheable, served with
Access-Control-Allow-Origin: * so browser-based clients can read it.
Notes on the current implementation:
response_types_supportedis onlycode; implicit and hybrid flows are not available.grant_types_supportedisauthorization_codeandrefresh_token: there is noclient_credentialsgrant.code_challenge_methods_supportedis onlyS256.- ID tokens are signed with
RS256and returned by the code exchange when the scope includesopenid;claims_supportedlists every claim they can carry. Therequest,request_uriandclaimsauthorization parameters are not supported. - For everything beyond the ID token claims (companies, module access) call
userinfowith the access token.
Responses
200Discovery document.application/json
HeadersAccess-Control-Allow-Origin
issuerstring (uri)authorization_endpointstring (uri)token_endpointstring (uri)userinfo_endpointstring (uri)jwks_uristring (uri)registration_endpointstring (uri)scopes_supportedarray of stringresponse_types_supportedarray of stringgrant_types_supportedarray of stringcode_challenge_methods_supportedarray of stringtoken_endpoint_auth_methods_supportedarray of stringresponse_modes_supportedarray of stringservice_documentationstring (uri)subject_types_supportedarray of stringid_token_signing_alg_values_supportedarray of stringOne ofRS256claims_supportedarray of stringrequest_parameter_supportedbooleanOne offalserequest_uri_parameter_supportedbooleanOne offalseclaims_parameter_supportedbooleanOne offalse
curl -X GET https://auth.nucleoplatform.com/.well-known/openid-configuration \
-H 'Accept: application/json'const res = await fetch("https://auth.nucleoplatform.com/.well-known/openid-configuration", {
method: "GET",
headers: {
Accept: "application/json",
},
});
const data = await res.json();
console.log(res.status, data);<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://auth.nucleoplatform.com/.well-known/openid-configuration', [
'headers' => [
'Accept' => 'application/json',
],
'http_errors' => false,
]);
echo $response->getStatusCode(), PHP_EOL;
echo $response->getBody();{
"issuer": "https://auth.nucleoplatform.com",
"authorization_endpoint": "https://auth.nucleoplatform.com/oauth/authorize",
"token_endpoint": "https://auth.nucleoplatform.com/oauth/token",
"userinfo_endpoint": "https://auth.nucleoplatform.com/api/oauth/userinfo",
"jwks_uri": "https://auth.nucleoplatform.com/.well-known/jwks.json",
"registration_endpoint": "https://auth.nucleoplatform.com/oauth/register",
"scopes_supported": [
"openid",
"profile",
"email",
"offline_access",
"nucleo.ai_tools"
],
"response_types_supported": [
"code"
],
"response_modes_supported": [
"query"
],
"grant_types_supported": [
"authorization_code",
"refresh_token"
],
"code_challenge_methods_supported": [
"S256"
],
"token_endpoint_auth_methods_supported": [
"client_secret_post",
"client_secret_basic",
"none"
],
"service_documentation": "https://nucleoplatform.com/developers",
"subject_types_supported": [
"public"
],
"id_token_signing_alg_values_supported": [
"RS256"
],
"claims_supported": [
"iss",
"sub",
"aud",
"azp",
"exp",
"iat",
"jti",
"auth_time",
"nonce",
"email",
"email_verified",
"name",
"picture",
"locale"
],
"request_parameter_supported": false,
"request_uri_parameter_supported": false,
"claims_parameter_supported": false
}/.well-known/jwks.jsonGet the token signing keys
JSON Web Key Set with the RSA public key that signs Nucleo access tokens and ID tokens (RS256).
The kid is the base64url SHA-256 of the public key, so it changes only when the key is rotated;
ID tokens carry it in their header. Use it to verify a token locally (signature, exp, aud =
your client_id). Public, with Access-Control-Allow-Origin: *. Returns {"keys": []} only if
the server has no key configured.
Responses
200Key set.application/json
keysarray of objectrequiredAttributes of each item
ktystringOne ofRSAusestringOne ofsigalgstringOne ofRS256kidstringnstringModulus
estringExponent
curl -X GET https://auth.nucleoplatform.com/.well-known/jwks.json \
-H 'Accept: application/json'const res = await fetch("https://auth.nucleoplatform.com/.well-known/jwks.json", {
method: "GET",
headers: {
Accept: "application/json",
},
});
const data = await res.json();
console.log(res.status, data);<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://auth.nucleoplatform.com/.well-known/jwks.json', [
'headers' => [
'Accept' => 'application/json',
],
'http_errors' => false,
]);
echo $response->getStatusCode(), PHP_EOL;
echo $response->getBody();{
"keys": [
{
"kty": "RSA",
"use": "sig",
"alg": "RS256",
"kid": "3rL1x0dXq6m0yBqH2l7xZr9n0bq8f4k1s2vJt8o5pWc",
"n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
"e": "AQAB"
}
]
}Client registration
Dynamic Client Registration (RFC 7591). Anonymous by design (as required by the MCP
authorization spec): the guard rails are on the metadata — https (or loopback) redirect URIs,
authorization code grant only, a fixed set of scopes. Self-registered clients are always
treated as third-party: the user approves them on a consent screen.
/oauth/registerRegister an OAuth client dynamically
RFC 7591 Dynamic Client Registration. No authentication: any client can register, the user's consent is what protects the account.
Rules applied to the metadata:
redirect_urisis required, 1 to 5 absolute URIs,httpsonly —httpis accepted only for loopback hosts (localhost,127.0.0.1,[::1], RFC 8252). No fragment, no*. Duplicates are removed.grant_typesdefaults to["authorization_code"]; onlyauthorization_codeandrefresh_tokenare accepted andauthorization_codemust be present.refresh_tokenis always added.response_typesmay only containcode.token_endpoint_auth_methoddefaults tonone(public client + PKCE, as MCP connectors work).client_secret_postorclient_secret_basicmake it a confidential client and the response includes aclient_secretthat never expires — it is shown only once.scopeis filtered (not rejected) to the supported set; when nothing valid is requested you get all of them.nucleo.ai_toolsis always added.client_nameis stripped of HTML and cut to 120 characters (default: the host of the first redirect URI);client_uriis cut to 250 characters. Other RFC 7591 fields are ignored.
Every call creates a new client (not idempotent). Clients registered here are third-party:
the user always sees the consent screen, and their tokens are limited to the AI tools
(nucleo.ai_tools).
Rate limit: 10 requests per minute per IP address.
Request bodyapplication/json
redirect_urisarray of string (uri)requiredhttps, orhttpon loopback only. No fragment, no wildcard.min items 1max items 5client_namestringShown on the consent screen. HTML is stripped.
max length 120client_uristring (uri)max length 250grant_typesarray of stringOne ofauthorization_coderefresh_tokendefault ["authorization_code"]response_typesarray of stringOne ofcodedefault ["code"]token_endpoint_auth_methodstringOne ofnoneclient_secret_postclient_secret_basicdefault nonescopestringSpace-separated; filtered to the supported scopes.
default openid profile email
Responses
201Client created.application/json
HeadersAccess-Control-Allow-Origin
client_idstringrequiredclient_id_issued_atintegerrequiredUnix timestamp.
client_secretstringConfidential clients only. Shown once.
client_secret_expires_atinteger0 = never expires.
One of0client_namestringredirect_urisarray of string (uri)requiredgrant_typesarray of stringrequiredresponse_typesarray of stringrequiredtoken_endpoint_auth_methodstringrequiredscopestringrequired
400Invalid client metadata (RFC 7591 §3.2.2 error response).application/json
HeadersAccess-Control-Allow-Origin
errorstringrequiredOne ofinvalid_redirect_uriinvalid_client_metadataerror_descriptionstring
429Rate limit exceeded.application/json
HeadersRetry-AfterX-RateLimit-LimitX-RateLimit-Remaining
messagestring
curl -X POST https://auth.nucleoplatform.com/oauth/register \
-H 'Content-Type: application/json' \
-d '{
"client_name": "Acme Assistant",
"redirect_uris": [
"https://assistant.acme.example/oauth/callback"
],
"grant_types": [
"authorization_code",
"refresh_token"
],
"response_types": [
"code"
],
"token_endpoint_auth_method": "none",
"scope": "openid profile email offline_access"
}'const res = await fetch("https://auth.nucleoplatform.com/oauth/register", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
"client_name": "Acme Assistant",
"redirect_uris": [
"https://assistant.acme.example/oauth/callback"
],
"grant_types": [
"authorization_code",
"refresh_token"
],
"response_types": [
"code"
],
"token_endpoint_auth_method": "none",
"scope": "openid profile email offline_access"
}),
});
const data = await res.json();
console.log(res.status, data);<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://auth.nucleoplatform.com/oauth/register', [
'json' => [
'client_name' => 'Acme Assistant',
'redirect_uris' => [
'https://assistant.acme.example/oauth/callback',
],
'grant_types' => [
'authorization_code',
'refresh_token',
],
'response_types' => [
'code',
],
'token_endpoint_auth_method' => 'none',
'scope' => 'openid profile email offline_access',
],
'http_errors' => false,
]);
echo $response->getStatusCode(), PHP_EOL;
echo $response->getBody();{
"client_id": "9d3f2b1c-6a4e-4f7b-9c2d-1e8a7b6c5d4f",
"client_id_issued_at": 1791100800,
"client_name": "Acme Assistant",
"redirect_uris": [
"https://assistant.acme.example/oauth/callback"
],
"grant_types": [
"authorization_code",
"refresh_token"
],
"response_types": [
"code"
],
"token_endpoint_auth_method": "none",
"scope": "openid profile email offline_access nucleo.ai_tools"
}{
"client_id": "9d3f2b1c-7b5f-4a8c-8d3e-2f9b8c7d6e5a",
"client_id_issued_at": 1791100800,
"client_secret": "s3cr3tExampleValueOnlyShownOnce000000000",
"client_secret_expires_at": 0,
"client_name": "Acme Reporting",
"redirect_uris": [
"https://reports.acme.example/auth/nucleo/callback"
],
"grant_types": [
"authorization_code",
"refresh_token"
],
"response_types": [
"code"
],
"token_endpoint_auth_method": "client_secret_basic",
"scope": "openid email nucleo.ai_tools"
}{
"error": "invalid_redirect_uri",
"error_description": "redirect_uris is required and must be a non-empty array."
}{
"error": "invalid_redirect_uri",
"error_description": "Too many redirect_uris (max 5)."
}{
"error": "invalid_redirect_uri",
"error_description": "redirect_uris must use https (http is allowed only for loopback addresses)."
}{
"error": "invalid_redirect_uri",
"error_description": "redirect_uris must not contain a fragment."
}{
"error": "invalid_redirect_uri",
"error_description": "Wildcards are not allowed in redirect_uris."
}{
"error": "invalid_client_metadata",
"error_description": "Unsupported grant_types: client_credentials. Only authorization_code and refresh_token are supported."
}{
"error": "invalid_client_metadata",
"error_description": "Only the \"code\" response_type is supported."
}{
"error": "invalid_client_metadata",
"error_description": "Unsupported token_endpoint_auth_method."
}{
"message": "Too Many Attempts."
}/oauth/registerCORS preflight for client registration
Lets browser-based clients register themselves. Always answers 204 with permissive CORS headers.
Responses
204Preflight accepted.
HeadersAccess-Control-Allow-OriginAccess-Control-Allow-MethodsAccess-Control-Allow-Headers
curl -X OPTIONS https://auth.nucleoplatform.com/oauth/registerconst res = await fetch("https://auth.nucleoplatform.com/oauth/register", {
method: "OPTIONS",
});
const data = await res.json();
console.log(res.status, data);<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('OPTIONS', 'https://auth.nucleoplatform.com/oauth/register', [
'http_errors' => false,
]);
echo $response->getStatusCode(), PHP_EOL;
echo $response->getBody();HTTP 204 — Preflight accepted.Tokens
Token endpoint and token revocation. Access tokens and ID tokens are RS256 JWTs signed with the key published in the JWKS; access tokens last 8 hours, ID tokens 1 hour. Refresh tokens last 30 days.
/oauth/tokenExchange a code or refresh a token
Standard OAuth 2.0 token endpoint (application/x-www-form-urlencoded or JSON).
- authorization_code: send
code,redirect_uri(same as in the authorize request),client_id, andcode_verifier(PKCE). Confidential clients also authenticate withclient_secret(body) or HTTP Basic. - refresh_token: send
refresh_tokenandclient_id(plus secret for confidential clients). A new refresh token is returned and the previous one is revoked — always store the latest one. - There is no
client_credentialsgrant: every token acts for a person.
ID token. When the granted scope includes openid, the authorization_code exchange also
returns id_token: an RS256 JWT signed with the JWKS key (kid in the header), valid for 1 hour.
Claims: iss (https://auth.nucleoplatform.com), sub (same as userinfo), aud and azp
(your client_id), iat, exp, jti, auth_time (when the person signed in to Nucleo), and
nonce if you sent one to /oauth/authorize. With email: email, email_verified. With
profile: name, locale, and picture when the person has an avatar. The refresh exchange
does not return a new ID token: keep the one from sign-in. An ID token is not an access token —
sending it as Bearer gets 401.
Lifetimes: access token 8 hours (expires_in: 28800), refresh token 30 days. For
self-registered clients the granted scope always includes nucleo.ai_tools.
Rate limit: 60 requests per minute per IP address.
Request bodyapplication/x-www-form-urlencoded, application/json
grant_typestringrequiredOne ofauthorization_coderefresh_tokenclient_idstringrequiredclient_secretstringConfidential clients using
client_secret_post.codestringauthorization_code only.
redirect_uristring (uri)authorization_code only; must equal the one used at
/oauth/authorize.code_verifierstringPKCE verifier; required when a
code_challengewas sent.min length 43max length 128refresh_tokenstringrefresh_token only.
scopestringOptional; on refresh it may only narrow the original scope.
Responses
200Tokens issued.application/json
token_typestringrequiredOne ofBearerexpires_inintegerrequiredExample:28800access_tokenstringrequiredRS256 JWT.
refresh_tokenstringid_tokenstringRS256 JWT (OpenID Connect ID token). Only on the
authorization_codeexchange when the scope includesopenid. Claims are described inIdTokenClaims.
400Invalid request, grant or scope.application/json
errorstringrequiredOne ofinvalid_requestinvalid_clientinvalid_grantunauthorized_clientunsupported_grant_typeinvalid_scopeaccess_deniederror_descriptionstringhintstring
401Client authentication failed.application/json
errorstringrequiredOne ofinvalid_requestinvalid_clientinvalid_grantunauthorized_clientunsupported_grant_typeinvalid_scopeaccess_deniederror_descriptionstringhintstring
429Rate limit exceeded.application/json
HeadersRetry-AfterX-RateLimit-LimitX-RateLimit-Remaining
messagestring
curl -X POST https://auth.nucleoplatform.com/oauth/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode grant_type=authorization_code \
--data-urlencode client_id=9d3f2b1c-6a4e-4f7b-9c2d-1e8a7b6c5d4f \
--data-urlencode redirect_uri=https://assistant.acme.example/oauth/callback \
--data-urlencode code=def50200a1b2c3 \
--data-urlencode code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXkconst res = await fetch("https://auth.nucleoplatform.com/oauth/token", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
grant_type: "authorization_code",
client_id: "9d3f2b1c-6a4e-4f7b-9c2d-1e8a7b6c5d4f",
redirect_uri: "https://assistant.acme.example/oauth/callback",
code: "def50200a1b2c3",
code_verifier: "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
}),
});
const data = await res.json();
console.log(res.status, data);<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://auth.nucleoplatform.com/oauth/token', [
'form_params' => [
'grant_type' => 'authorization_code',
'client_id' => '9d3f2b1c-6a4e-4f7b-9c2d-1e8a7b6c5d4f',
'redirect_uri' => 'https://assistant.acme.example/oauth/callback',
'code' => 'def50200a1b2c3',
'code_verifier' => 'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk',
],
'http_errors' => false,
]);
echo $response->getStatusCode(), PHP_EOL;
echo $response->getBody();{
"token_type": "Bearer",
"expires_in": 28800,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiI5ZDNmMmIxYy02YTRlIiwic3ViIjoiNDIiLCJzY29wZXMiOlsib3BlbmlkIiwibnVjbGVvLmFpX3Rvb2xzIl19.signature",
"refresh_token": "def50200f9e8d7c6b5a4",
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjNyTDF4MGRYcTZtMHlCcUgybDd4WnI5bjBicThmNGsxczJ2SnQ4bzVwV2MifQ.eyJpc3MiOiJodHRwczovL2F1dGgubnVjbGVvcGxhdGZvcm0uY29tIiwic3ViIjoiNDIiLCJhdWQiOiI5ZDNmMmIxYy02YTRlLTRmN2ItOWMyZC0xZThhN2I2YzVkNGYiLCJhenAiOiI5ZDNmMmIxYy02YTRlLTRmN2ItOWMyZC0xZThhN2I2YzVkNGYiLCJpYXQiOjE3OTExMDA4MDAsImV4cCI6MTc5MTEwNDQwMCwianRpIjoiMGI2ZjFkMmUtNGMzYS00ZThiLTlmN2QtMmExYzNlNWI3ZDlmIiwiYXV0aF90aW1lIjoxNzkxMTAwNzUwLCJub25jZSI6Im4tMFM2X1d6QTJNaiIsImVtYWlsIjoiZ2l1bGlhLnJvc3NpQGFjbWUuZXhhbXBsZSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlfQ.signature"
}{
"error": "invalid_grant",
"error_description": "The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.",
"hint": "Failed to verify `code_verifier`."
}{
"error": "unsupported_grant_type",
"error_description": "The authorization grant type is not supported by the authorization server.",
"hint": "Check that all required parameters have been provided"
}{
"error": "invalid_client",
"error_description": "Client authentication failed"
}{
"message": "Too Many Attempts."
}/api/auth/revokeRevoke the current access token
Revokes the access token sent in Authorization and every refresh token issued with it
(sign-out / disconnect). Idempotent from the client's point of view: a revoked token then
answers 401, on the MCP connector immediately as well. Available to self-registered clients'
tokens. The confirmation message is currently returned in Italian.
Responses
200Token revoked.application/json
messagestring
401Missing, expired or revoked access token.application/json
messagestring
curl -X POST https://auth.nucleoplatform.com/api/auth/revoke \
-H "Authorization: Bearer $NUCLEO_ACCESS_TOKEN" \
-H 'Accept: application/json'const res = await fetch("https://auth.nucleoplatform.com/api/auth/revoke", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.NUCLEO_ACCESS_TOKEN}`,
Accept: "application/json",
},
});
const data = await res.json();
console.log(res.status, data);<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://auth.nucleoplatform.com/api/auth/revoke', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('NUCLEO_ACCESS_TOKEN'),
'Accept' => 'application/json',
],
'http_errors' => false,
]);
echo $response->getStatusCode(), PHP_EOL;
echo $response->getBody();{
"message": "Token revocato."
}{
"message": "Unauthenticated."
}Identity
The signed-in person, their companies and module access (OpenID Connect UserInfo).
/api/oauth/userinfoGet the signed-in user
OpenID Connect UserInfo. Returns who the token belongs to, the companies (organizations) they belong to and the Nucleo modules they can use, with their role.
subis the stable user id: key your local records on it, never onemail.orgs[].idis the company id to pass asX-Nucleo-Companyto the AI tools.apps[]lists module access per company (org_id: null= global access for Nucleo staff). During the module migration legacy slugs may appear next tobrain,catalog,commerce,intelligence: ignore slugs you do not know.disabled_sectionslists, per company id, the sub-modules switched off for that company.scopeis the effective scope of the token:nucleo.ai_toolsmarks a token of a self-registered (AI) client.impersonatoris non-null only when Nucleo staff is acting as the user.
Available to self-registered clients' tokens.
Responses
200The user.application/json
substringrequiredStable user id.
namestringemailstring (email)requiredemail_verifiedbooleanavatar_urlstring (uri) | nulllanguagestringExample:enthemestringOne oflightdarkis_internalbooleanTrue only for Nucleo staff with access to every company.
is_ownerbooleanorg_idsarray of integerorgsarray of objectAttributes of each item
idintegerslugstringnamestringlogo_urlstring (uri) | nullrolestringExample:admin
app_accessarray of stringModule slugs the user can open.
appsarray of objectAttributes of each item
slugstringExample:catalogrolestringExample:editororg_idinteger | null
disabled_sectionsobjectCompany id → list of disabled sub-module slugs.
client_idstring | nullscopestringEffective scopes of the token
impersonatorobject | nullChild attributes
idintegernamestringemailstring (email)expires_atstring (date-time)
401Missing, expired or revoked access token.application/json
messagestring
curl -X GET https://auth.nucleoplatform.com/api/oauth/userinfo \
-H "Authorization: Bearer $NUCLEO_ACCESS_TOKEN" \
-H 'Accept: application/json'const res = await fetch("https://auth.nucleoplatform.com/api/oauth/userinfo", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.NUCLEO_ACCESS_TOKEN}`,
Accept: "application/json",
},
});
const data = await res.json();
console.log(res.status, data);<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://auth.nucleoplatform.com/api/oauth/userinfo', [
'headers' => [
'Authorization' => 'Bearer ' . getenv('NUCLEO_ACCESS_TOKEN'),
'Accept' => 'application/json',
],
'http_errors' => false,
]);
echo $response->getStatusCode(), PHP_EOL;
echo $response->getBody();{
"sub": "42",
"name": "Giulia Rossi",
"email": "giulia.rossi@acme.example",
"email_verified": true,
"avatar_url": "https://cdn.acme.example/avatars/42.png",
"language": "it",
"theme": "light",
"is_internal": false,
"is_owner": false,
"org_ids": [
7
],
"orgs": [
{
"id": 7,
"slug": "acme",
"name": "Acme Apparel",
"logo_url": "https://cdn.acme.example/logo.png",
"role": "admin"
}
],
"app_access": [
"catalog",
"commerce"
],
"apps": [
{
"slug": "catalog",
"role": "editor",
"org_id": 7
},
{
"slug": "commerce",
"role": "viewer",
"org_id": 7
}
],
"disabled_sections": {
"7": [
"pos"
]
},
"client_id": "9d3f2b1c-6a4e-4f7b-9c2d-1e8a7b6c5d4f",
"scope": "openid profile email offline_access nucleo.ai_tools",
"impersonator": null
}{
"message": "Unauthenticated."
}Organization branding
Public, unauthenticated branding of a company (name, logos, favicons), used by Nucleo's public pages (for example booking pages) that have no signed-in user but must carry the company's brand.
/api/public/organizations/{slug}/brandingGet a company's public branding
Name, logos (light and dark) and favicons of a company, by slug. Only data that is public by
nature (logo URLs point to public storage) — never members, domains or billing. Cached for
5 minutes on the server and sent with Cache-Control: public, max-age=300.
Rate limit: 120 requests per minute per IP address.
Path parameters
slugstringrequiredCompany slug, lowercase. Must match
^[a-z0-9][a-z0-9-]{0,63}$, otherwise404.max length 64pattern ^[a-z0-9][a-z0-9-]{0,63}$Example:acme
Responses
200Branding.application/json
HeadersCache-Control
dataOrganizationBrandingrequiredChild attributes
slugstringnamestringlogo_urlstring (uri) | nulllogo_dark_urlstring (uri) | nullfavicon_urlstring (uri) | nullfavicon_light_urlstring (uri) | null
404Unknown or invalid slug.application/json
messagestring
429Rate limit exceeded.application/json
HeadersRetry-AfterX-RateLimit-LimitX-RateLimit-Remaining
messagestring
curl -X GET https://auth.nucleoplatform.com/api/public/organizations/acme/branding \
-H 'Accept: application/json'const res = await fetch("https://auth.nucleoplatform.com/api/public/organizations/acme/branding", {
method: "GET",
headers: {
Accept: "application/json",
},
});
const data = await res.json();
console.log(res.status, data);<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://auth.nucleoplatform.com/api/public/organizations/acme/branding', [
'headers' => [
'Accept' => 'application/json',
],
'http_errors' => false,
]);
echo $response->getStatusCode(), PHP_EOL;
echo $response->getBody();{
"data": {
"slug": "acme",
"name": "Acme Apparel",
"logo_url": "https://cdn.acme.example/brand/logo.svg",
"logo_dark_url": "https://cdn.acme.example/brand/logo-dark.svg",
"favicon_url": "https://cdn.acme.example/brand/favicon.png",
"favicon_light_url": null
}
}{
"message": "Not found"
}{
"message": "Too Many Attempts."
}