Authentication
All API requests must be authenticated. Angelcam supports two methods:
- OAuth2 — for integrations and commercial use
- Personal Access Token — for personal/development use only
OAuth2
Section titled “OAuth2”OAuth2 produces a short-lived Bearer token. Pass it with every request:
curl -H "Authorization: Bearer ACCESS_TOKEN" \ -X GET "https://api.angelcam.com/v1/cameras/?limit=5"Tokens expire after 10 hours and must be refreshed using a refresh token. Each refresh returns the same refresh token; it expires if it is not used for 30 days.
Create your OAuth2 app in the MyAngelcam Dashboard to get your Client ID (and, for confidential applications, a Client Secret).
Public vs. confidential applications
Section titled “Public vs. confidential applications”When you create an application, choose its type based on where it runs. You cannot change the type later.
| Confidential | Public | |
|---|---|---|
| Runs on | A server you control — a web backend or an internal service | The user’s device — a mobile, desktop or single-page app |
| Client secret | Yes, shown once when you create the application | No — the app cannot keep a secret private |
Sends to /oauth/token/ | client_id + client_secret + code_verifier | client_id + code_verifier |
Both types use the authorization code flow with PKCE.
Important: Never put a client secret in a mobile, desktop or browser app — anyone can extract it. Use a public application instead.
Authorization code flow with PKCE
Section titled “Authorization code flow with PKCE”The user authenticates through Angelcam’s login page — similar to “Sign in with Google” — and your app receives an authorization code that it exchanges for tokens.
PKCE (Proof Key for Code Exchange) ties the authorization code to the app that requested it, so an intercepted code cannot be used by anyone else. PKCE is required for both public and confidential applications.
Step 1 — generate a code verifier and its code challenge.
The code_verifier is a random string of 43–128 characters (A-Z, a-z, 0-9, -, ., _, ~). The code_challenge is its SHA-256 hash, base64url-encoded without padding. Generate a new pair for every authorization request and keep the verifier until Step 3.
function base64UrlEncode(bytes) { return btoa(String.fromCharCode(...bytes)) .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');}
const codeVerifier = base64UrlEncode(crypto.getRandomValues(new Uint8Array(32)));const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier));const codeChallenge = base64UrlEncode(new Uint8Array(digest));Or in a shell:
CODE_VERIFIER=$(openssl rand -base64 32 | tr '+/' '-_' | tr -d '=')CODE_CHALLENGE=$(printf '%s' "$CODE_VERIFIER" | openssl dgst -sha256 -binary | base64 | tr '+/' '-_' | tr -d '=')Step 2 — redirect the user to:
GET https://my.angelcam.com/oauth/authorize/ ?response_type=code &client_id=CLIENT_ID &redirect_uri=https://yourapp.com/callback &scope=user_access &state=CSRF_TOKEN &code_challenge=CODE_CHALLENGE &code_challenge_method=S256Use a random state and check that it matches when the user comes back. Requests without code_challenge are rejected with error=invalid_request.
The user will be asked to approve your app:

Step 3 — Angelcam redirects back to your redirect_uri with code and state. Exchange the code together with the code_verifier from Step 1.
Confidential application (from your server):
curl -X POST https://my.angelcam.com/oauth/token/ \ -d grant_type=authorization_code \ -d client_id=CLIENT_ID \ -d client_secret=CLIENT_SECRET \ -d code=AUTH_CODE \ -d redirect_uri=https://yourapp.com/callback \ -d code_verifier=CODE_VERIFIERPublic application (from the user’s device — no client secret):
curl -X POST https://my.angelcam.com/oauth/token/ \ -d grant_type=authorization_code \ -d client_id=CLIENT_ID \ -d code=AUTH_CODE \ -d redirect_uri=https://yourapp.com/callback \ -d code_verifier=CODE_VERIFIERThe /oauth/ endpoints allow cross-origin requests, so a single-page app can call them directly from the browser.
Response:
{ "access_token": "...", "refresh_token": "...", "token_type": "Bearer", "expires_in": 36000, "scope": "user_access"}Note: Applications created before PKCE was introduced keep working without
code_challengeandcode_verifier. We recommend adding PKCE to them as well.
Refreshing a token
Section titled “Refreshing a token”When a request returns 401 Unauthorized, refresh the access token. Confidential applications must also send their client_secret:
curl -X POST https://my.angelcam.com/oauth/token/ \ -d grant_type=refresh_token \ -d client_id=CLIENT_ID \ -d client_secret=CLIENT_SECRET \ -d refresh_token=REFRESH_TOKENPublic applications send only client_id and refresh_token.
To revoke a token, use the oauth/revoke-token/ endpoint.
Password grant type
Section titled “Password grant type”Legacy: New applications use the authorization code flow with PKCE. The password grant is only available to applications that already have it enabled.
Your app collects the user’s credentials and exchanges them directly for a token.

Important: Never store the user’s password. For “remember me”, store the refresh token instead.
curl -X POST https://my.angelcam.com/oauth/token/ \ -d "client_id=CLIENT_ID&scope=user_access&grant_type=password&username=user%40example.com&password=PASSWORD"Confidential applications must also send client_secret.
The response has the same format as in the authorization code flow.
Personal Access Token
Section titled “Personal Access Token”For personal use only. No expiration, but can be revoked manually.
Obtain your token in the MyAngelcam Dashboard.
curl -H "Authorization: PersonalAccessToken YOUR_TOKEN" \ -X GET "https://api.angelcam.com/v1/me/"