Skip to content

Guides

Connect an app (OAuth 2.1)

When your app acts on behalf of a SkedCast user, use OAuth 2.1 — the Authorization Code flow with PKCE (S256, mandatory). Clients register dynamically (no manual app setup) and discover every endpoint from the metadata document. This is the end-to-end flow with copy-paste examples. (For your own server-side scripts, an API key is simpler — see Authentication.)

Developer docs

8 min read

The flow at a glance

  • Discover the authorization server from the well-known metadata.
  • Register your client once (Dynamic Client Registration) to get a client_id.
  • Generate a PKCE verifier + challenge per authorization.
  • Redirect the user to /oauth/authorize; they sign in and consent.
  • Exchange the returned code at /oauth/token for an access + refresh token.
  • Call the API; refresh when the access token expires; revoke on logout.

1. Discover the authorization server

Everything you need is in the RFC 8414 metadata document — endpoints, supported scopes, and the PKCE method. Read endpoints from it rather than hard-coding paths.

curl https://api.skedcast.com/.well-known/oauth-authorization-server

2. Register your client (DCR)

Register once (RFC 7591) to get a client_id. SkedCast issues PUBLIC clients only (PKCE, no client secret). Keep the registration_access_token if you want to update or delete the registration later (RFC 7592).

curl -X POST https://api.skedcast.com/oauth/register \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "My App",
    "redirect_uris": ["https://myapp.com/oauth/callback"],
    "scope": "posts.read posts.compose"
  }'
redirect_uris must be https (or http loopback for native / CLI clients).
{
  "client_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "client_id_issued_at": 1751299200,
  "redirect_uris": ["https://myapp.com/oauth/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_method": "none",
  "scope": "posts.read posts.compose",
  "registration_access_token": "reg_…",
  "registration_client_uri": "https://api.skedcast.com/oauth/register/f47ac10b-…"
}
Response — keep client_id; the rest is echoed back.

3. Generate a PKCE pair

Per authorization, create a high-entropy code_verifier and its S256 code_challenge (RFC 7636). Keep the verifier private; put the challenge in the authorize URL.

import { randomBytes, createHash } from "node:crypto";

// code_verifier: 43-128 unreserved chars (RFC 7636 §4.1)
const codeVerifier = randomBytes(32).toString("base64url");

// code_challenge = base64url(SHA-256(verifier)) (RFC 7636 §4.2)
const codeChallenge = createHash("sha256")
  .update(codeVerifier)
  .digest("base64url");
PKCE (Node) — verifier stays server-side, challenge goes in the URL.

4. Send the user to authorize

Redirect the user's browser to /oauth/authorize with your client_id, scopes, a random state (CSRF), and the PKCE challenge. They sign in and consent; SkedCast redirects back to your redirect_uri with ?code=…&state=…. Verify state matches what you sent.

GET https://api.skedcast.com/oauth/authorize
  ?response_type=code
  &client_id=f47ac10b-…
  &redirect_uri=https://myapp.com/oauth/callback
  &scope=posts.read%20posts.compose
  &state=<random-csrf-token>
  &code_challenge=<codeChallenge>
  &code_challenge_method=S256
The authorize URL (line-wrapped for readability).

5. Exchange the code for tokens

POST the code back with your code_verifier. SkedCast verifies the PKCE challenge and returns an access + refresh token.

curl -X POST https://api.skedcast.com/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d grant_type=authorization_code \
  -d code=<authorization_code> \
  -d redirect_uri=https://myapp.com/oauth/callback \
  -d client_id=f47ac10b-… \
  -d code_verifier=<codeVerifier>
{
  "access_token": "eyJ…",
  "token_type": "Bearer",
  "expires_in": 900,
  "scope": "posts.read posts.compose",
  "refresh_token": "eyJ…"
}
Token response.

6. Call the API

Send the access token as a Bearer credential — exactly like an API key. Its power is still scopes ∩ the user's role.

curl https://api.skedcast.com/v1/agency \
  -H "Authorization: Bearer eyJ…"

7. Refresh when it expires

Access tokens last 15 minutes. Exchange your refresh token for a new pair before, or when a call returns 401.

curl -X POST https://api.skedcast.com/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d grant_type=refresh_token \
  -d refresh_token=eyJ…

8. Revoke on logout

When the user disconnects, revoke the refresh token (RFC 7009) — this kills the whole rotation family. The endpoint always returns 200, even for an unknown token, so it never leaks token state.

curl -X POST https://api.skedcast.com/oauth/revoke \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d token=eyJ…
oauthpkceauthenticationdcr

FAQ

Do I need a client secret?
No. SkedCast registers public clients only and requires PKCE (S256), so there is no client secret to store — the code_verifier proves possession at the token step.
What is the resource parameter for?
OAuth access tokens are audience-bound (RFC 8707). The optional resource parameter on /oauth/authorize and /oauth/token defaults to the SkedCast resource, so you can omit it; the metadata document lists the value if you want to set it explicitly.

Be first in line when SkedCast opens

Join the waitlist — agencies on it get early access and launch-day onboarding.