Cello · Authentication Profile
Cello Authentication
Authentication
Cello secures its APIs with http and oauth2 across 3 declared security schemes, as derived from its OpenAPI definitions.
CompanyReferral MarketingAffiliate MarketingGrowthSaaSAttributionPartner ProgramsAi Enterprise Software
Methods: http, oauth2
Schemes: 3
OAuth flows:
API key in:
Security Schemes
bearerAuth http
scheme: bearer
mcp-oauth oauth2
· flows: , , ,
component-jwt jwt
Source
Authentication Profile
generated: '2026-08-13'
method: searched
source:
- https://docs.cello.so/api-reference/introduction
- https://docs.cello.so/api-reference/authentication/get-token
- https://mcp.cello.so/.well-known/oauth-protected-resource
- https://auth.cello.so/.well-known/oauth-authorization-server
- openapi/cello-token-api-openapi.yml
docs: https://docs.cello.so/api-reference/introduction
summary:
types:
- http
- oauth2
note: >-
Cello runs two independent authentication systems. The REST API uses a credential exchange —
POST /token with accessKeyId + secretAccessKey returns a short-lived bearer accessToken — and
declares only bearerAuth in the OpenAPI. The hosted MCP server uses full OAuth 2.0 against
https://auth.cello.so, with RFC 9728 protected-resource metadata, dynamic client registration
and PKCE. Neither system's credentials work on the other surface. A third mechanism, a
product-signed JWT, authenticates end users into the embedded web and mobile components.
schemes:
- name: bearerAuth
type: http
scheme: bearer
surface: rest
hosts:
- https://api.cello.so
- https://api.sandbox.cello.so
credential_exchange:
endpoint: POST /token
unauthenticated: true
request_fields:
- accessKeyId
- secretAccessKey
refresh_fields:
- refreshToken
response_fields:
- accessToken
- refreshToken
- expiresIn
- refreshTokenExpiresIn
access_token_ttl_seconds: 18000
refresh_token_ttl_seconds: 432000
ttl_source: >-
Published response examples in the OpenAPI (expiresIn 18000 = 5 hours,
refreshTokenExpiresIn 432000 = 5 days).
issued_from: Cello Portal -> Integrations -> Access Keys (https://portal.cello.so/integrations/accesskeys)
environment_scoped: true
scopes: []
sources:
- openapi/cello-token-api-openapi.yml
- openapi/cello-events-api-openapi.yml
- openapi/cello-new-users-api-openapi.yml
- openapi/cello-referral-codes-api-openapi.yml
- openapi/cello-referrers-api-openapi.yml
- name: mcp-oauth
type: oauth2
surface: mcp
hosts:
- https://mcp.cello.so
- https://mcp.sandbox.cello.so
authorization_server: https://auth.cello.so
flows:
- authorization_code
- client_credentials
- refresh_token
- device_code
pkce: S256
dynamic_client_registration: https://auth.cello.so/oauth2/register
scopes:
- mcp:read
- mcp:write
discovery:
protected_resource: https://mcp.cello.so/.well-known/oauth-protected-resource
authorization_server: https://auth.cello.so/.well-known/oauth-authorization-server
ref: ../scopes/cello-scopes.yml
note: >-
Not declared in any OpenAPI — discovered by probing the MCP endpoint, which answers an
unauthenticated tools/list with 401 and a WWW-Authenticate resource_metadata pointer.
- name: component-jwt
type: jwt
surface: embedded-components
signed_with: Product Secret (Cello Portal -> Integrations -> Access Keys)
identifies: the end user, via productUserId
used_by:
- Cello JS (web Referral Component)
- iOS / Android / React Native / Flutter SDKs
note: >-
A product-signed JWT boots the Referral Component for a specific end user. It authenticates a
user into the widget, not a server into the API, and is documented at
https://docs.cello.so/sdk/client-side/user-authentication rather than in the API reference.
observations:
- id: no-oauth-on-rest
detail: >-
The REST API publishes no OAuth metadata on api.cello.so — /.well-known/oauth-authorization-server
and /.well-known/openid-configuration both 404 — and the mcp:read / mcp:write scopes do not
apply to it. There is no scoped, least-privilege credential for the REST surface: an
accessToken is all-or-nothing across all six operations, including the destructive
POST /referrers/{productUserId}/depersonalize.
- id: unauthenticated-401-shapes-differ
detail: >-
The two REST error envelopes disagree. An unauthenticated resource call returns
{"message":"Unauthorized"}; a bad credential exchange at POST /token returns
{"statusCode":401,"timestamp":"...","path":"/api/token","message":"Invalid API Credentials"} —
a richer NestJS-style envelope, and one that leaks an internal path prefix (/api/token) the
public route does not use.
evidence:
- url: https://api.cello.so/referral-codes/TESTCODE123
status: 401
- url: https://api.cello.so/token
status: 401
x-evidence:
fetched: '2026-08-13'
probes:
- {url: 'https://docs.cello.so/api-reference/authentication/get-token.md', http_status: 200}
- {url: 'https://mcp.cello.so/.well-known/oauth-protected-resource', http_status: 200}
- {url: 'https://auth.cello.so/.well-known/oauth-authorization-server', http_status: 200}
- {url: 'https://api.cello.so/.well-known/oauth-authorization-server', http_status: 404}