Skip to content

Authentication

Homecast supports two authentication models for programmatic access.

ModelFormatUse caseLifetime
Access Tokenhc_...Programmatic API scriptsUntil revoked or expired
OAuth 2.1Bearer tokenThird-party integrations, MCP1 hour (refreshable)

Both are sent the same way:

http
Authorization: Bearer <token>

Access tokens

Programmatic tokens for scripts and automations. Prefixed with hc_. Enable Developer Mode in SettingsAccount, then manage tokens from SettingsAPI AccessManage.

API tokens in settings

Create a token

Create token form

graphql
mutation {
  createAccessToken(
    name: "My Script"
    homePermissions: "{\"home-uuid\": \"control\"}"
  ) {
    success
    token
    accessToken {
      id
      name
      tokenPrefix
    }
    error
  }
}

The full token value (starting with hc_) is only returned on creation. Store it immediately.

Home permissions

The homePermissions field is a JSON string mapping home UUIDs to access levels:

json
{
  "550e8400-e29b-41d4-a716-446655440000": "control",
  "6ba7b810-9dad-11d1-80b4-00c04fd430c8": "view"
}
LevelCan read stateCan control devices
controlYesYes
viewYesNo

If homePermissions is null or omitted, the token has full access to all homes.

Token properties

PropertyDescription
idToken UUID
nameUser-defined label
tokenPrefixFirst characters of token (for identification)
lastUsedAtLast time the token was used
expiresAtExpiration date (null = never expires)
revokedAtRevocation date (null = active)
createdAtCreation timestamp

List and revoke

graphql
# List all tokens
{ accessTokens { id name tokenPrefix lastUsedAt expiresAt createdAt } }

# Revoke a token
mutation { revokeAccessToken(tokenId: "token-uuid") { success error } }

OAuth 2.1

Full OAuth 2.1 with PKCE for third-party integrations and MCP clients.

Discovery

EndpointPurpose
GET /.well-known/oauth-authorization-serverServer metadata (RFC 8414)
GET /.well-known/openid-configurationOpenID Connect Discovery
GET /.well-known/oauth-protected-resourceResource metadata (RFC 8707)

Server metadata response:

json
{
  "issuer": "https://api.homecast.cloud",
  "authorization_endpoint": "https://api.homecast.cloud/oauth/authorize",
  "token_endpoint": "https://api.homecast.cloud/oauth/token",
  "registration_endpoint": "https://api.homecast.cloud/oauth/register",
  "scopes_supported": ["mcp:read", "mcp:write", "mcp:admin"],
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "code_challenge_methods_supported": ["S256"],
  "token_endpoint_auth_methods_supported": ["none", "client_secret_post"]
}

Scopes

ScopeDescription
mcp:readRead device state
mcp:writeRead and control devices
mcp:adminFull access including management

Flow

StepFromToAction
1AppHomecastPOST /oauth/register
2HomecastAppReturns client_id
3AppUserRedirect to /oauth/authorize
4UserHomecastLogin + consent
5HomecastUserRedirect with ?code=...
6UserAppAuthorization code
7AppHomecastPOST /oauth/token (code + PKCE verifier)
8HomecastAppaccess_token + refresh_token

The user sees a consent screen where they choose which homes to share and the permission level:

OAuth consent screen

1. Dynamic client registration

http
POST /oauth/register
Content-Type: application/json

{
  "redirect_uris": ["https://your-app.com/callback"],
  "client_name": "My Integration",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none"
}

Response:

json
{
  "client_id": "generated-uuid",
  "client_secret": "generated-secret",
  "client_id_issued_at": 1708099200,
  "client_secret_expires_at": 0,
  "redirect_uris": ["https://your-app.com/callback"],
  "client_name": "My Integration",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none"
}

2. Authorization request (with PKCE)

GET /oauth/authorize?
  client_id={client_id}&
  redirect_uri=https://your-app.com/callback&
  response_type=code&
  code_challenge={challenge}&
  code_challenge_method=S256&
  scope=mcp:write&
  state={random_state}

The user logs in, selects which homes to grant access to, and approves. Homecast redirects back:

https://your-app.com/callback?code={auth_code}&iss=https://api.homecast.cloud&state={random_state}

3. Token exchange

http
POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code={auth_code}&
redirect_uri=https://your-app.com/callback&
code_verifier={verifier}

Response:

json
{
  "access_token": "eyJ...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "refresh_token_value",
  "scope": "mcp:write"
}

4. Refresh

http
POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&
refresh_token={refresh_token}

Returns a new access token and a rotated refresh token.

Token lifetimes

TokenLifetime
Authorization code10 minutes
Access token1 hour
Refresh token30 days

Refresh tokens use rotation — each refresh returns a new refresh token and invalidates the old one.

Managing authorized apps

View and revoke authorized apps from SettingsShared ItemsAuthorized Apps in the dashboard.

Authorized apps

graphql
# List apps with OAuth access
{ authorizedApps { clientId clientName scope createdAt } }

# Revoke an app's access
mutation { revokeAuthorizedApp(clientId: "client-uuid") { success error } }

Error responses

StatusBodyMeaning
401{"error": "invalid_token"}Token is expired, revoked, or malformed
403{"error": "insufficient_permissions"}Token doesn't have access to the requested home or action
401{"error": "invalid_client"}OAuth client credentials are invalid
400{"error": "invalid_grant"}Authorization code or refresh token is invalid/expired