Help API

    Authentication

    Every request to /v1/* must carry a bearer token:

    Authorization: Bearer <token>
    

    Two token kinds are accepted:

    Prefix Type Use for
    42min_pat_… Personal access token (PAT) Server-side scripts and integrations that act as your own account.
    42min_at_… OAuth 2.1 access token A third-party app acting on behalf of a 42min user.

    There is no other auth surface — cookies, session JWTs, and Basic auth are not accepted on /v1/*. Missing or malformed credentials return 401 invalid_token with a WWW-Authenticate: Bearer … header.

    Personal access tokens (PATs)

    PATs are long-lived bearer tokens scoped to your own account. Use them for internal scripts, cron jobs, server-side automations — anywhere the caller is you.

    Token format

    42min_pat_<lookup12>_<secret32>
    
    • 42min_pat_ — fixed prefix that identifies the token family.
    • <lookup12> — 12-character public lookup id (Crockford base32). Used only to find the row; not a secret.
    • <secret32> — 32-character secret. Hashed at rest with HMAC-SHA256 keyed by a server pepper. Shown once at creation — store it immediately.

    Mint a PAT

    1. Open Admin Center → API and switch to the API keys tab. You need the Owner or Admin role.
    2. Click New API key. Give it a name, pick the scopes you want it to hold, and optionally set an expiry.
    3. Copy the token that appears — it is shown once and never again. The dashboard only ever displays the last four characters afterward.

    Limits:

    • 42 active keys per account.
    • Key names are unique per account (case-insensitive).
    • Adding scopes to an existing key requires re-confirming your account password.
    curl -H "Authorization: Bearer 42min_pat_ABCDEFGHJKMN_PQRSTVWXYZ23456789ABCDEFGHJK" \
      https://api.42min.us/v1/me
    

    Rotation and revocation

    • Regenerate issues a new secret on the same key id; the old secret is revoked immediately.
    • Delete revokes a key. Once revoked, requests with that token return 401 token_revoked.
    • Expired keys return 401 token_expired.

    PATs are owned by the user who created them and inherit that user's account membership. If the user is removed from the account, the keys they own stop working.

    OAuth 2.1 + PKCE

    For third-party applications that act on behalf of a 42min user, use the OAuth 2.1 authorization code flow with PKCE (S256). PKCE is mandatory for every client — public and confidential alike.

    Discovery is published at /.well-known/oauth-authorization-server.

    Endpoint Path
    Authorization GET https://api.42min.us/v1/oauth/authorize
    Token POST https://api.42min.us/v1/oauth/token
    Revocation POST https://api.42min.us/v1/oauth/revoke
    Introspection POST https://api.42min.us/v1/oauth/introspect

    Register an OAuth client

    1. In Admin Center → API, open the OAuth apps tab. Owner or Admin role required.
    2. Click New OAuth app and fill in:
      • Name — shown to users on the consent screen.
      • Client typeconfidential (server-side, can hold a secret) or public (browser/mobile, uses PKCE only).
      • Redirect URIs — exact match required at authorize time. https:// only, except http://localhost and http://127.0.0.1 for development.
      • Allowed scopes — the superset of scopes the app can ever request. Per-grant scopes must be a subset of this list.
    3. Save. You get back:
      • client_id42min_<24 chars> — not secret.
      • client_secret42min_cs_<48 chars>, shown once (confidential clients only).
      • Rotate secret issues a new one and revokes the old one immediately.

    The full flow

    your app ──► 1. /v1/oauth/authorize ──► 302 to 42min.us/oauth/consent
                                                  │
                                                  ▼
            user logs in & approves on consent screen
                                                  │
                                                  ▼
    your app ◄── 2. 302 redirect_uri?code=42min_ac_…&state=…
    your app ──► 3. POST /v1/oauth/token (code + code_verifier)
    your app ◄── 4. { access_token: "42min_at_…", refresh_token: "42min_rt_…", expires_in: 3600 }
    

    1. Build the PKCE verifier and challenge

    Generate a random code_verifier (43–128 URL-safe characters) and derive code_challenge = BASE64URL(SHA256(code_verifier)). Persist the verifier in the user's session — you need it at step 3.

    2. Send the user to /v1/oauth/authorize

    https://api.42min.us/v1/oauth/authorize
      ?response_type=code
      &client_id=42min_…
      &redirect_uri=https://yourapp.example.com/callback
      &scope=event_types:read%20slots:read%20bookings:write
      &code_challenge=<challenge>
      &code_challenge_method=S256
      &state=<csrf-token>
    

    Required parameters:

    • response_type=code — the only response type supported.
    • client_id
    • redirect_uri — must exactly match one of the URIs registered on the client.
    • code_challenge and code_challenge_method=S256 — anything else is rejected.
    • scope — space-separated list of scopes; must be a subset of the client's allowed_scopes.
    • state — opaque value echoed back on redirect. Use it to bind the response to the user's session and to defend against CSRF.

    42min redirects to its in-product consent screen, where the user signs in (if needed) and approves or denies the request. After the decision, 42min redirects to your redirect_uri:

    https://yourapp.example.com/callback?code=42min_ac_…&state=<csrf-token>
    

    On denial:

    https://yourapp.example.com/callback?error=access_denied&state=<csrf-token>
    

    Authorization codes live for 10 minutes and are single-use.

    3. Exchange the code at /v1/oauth/token

    curl -X POST https://api.42min.us/v1/oauth/token \
      -H 'Content-Type: application/x-www-form-urlencoded' \
      --data-urlencode grant_type=authorization_code \
      --data-urlencode code=42min_ac_… \
      --data-urlencode redirect_uri=https://yourapp.example.com/callback \
      --data-urlencode client_id=42min_… \
      --data-urlencode code_verifier=<verifier> \
      --data-urlencode client_secret=42min_cs_…   # confidential clients only
    

    Confidential clients may authenticate with HTTP Basic instead of body parameters: Authorization: Basic base64(client_id:client_secret).

    Response:

    {
      "access_token": "42min_at_…",
      "refresh_token": "42min_rt_…",
      "token_type": "Bearer",
      "expires_in": 3600,
      "scope": "event_types:read slots:read bookings:create bookings:cancel bookings:reschedule bookings:update"
    }
    
    • Access tokens live for 1 hour.
    • Refresh tokens live for 60 days and are rotated on every use.
    • The scope claim reflects the expanded set (aliases like bookings:write are unrolled). See Scopes.

    4. Refresh

    curl -X POST https://api.42min.us/v1/oauth/token \
      -H 'Content-Type: application/x-www-form-urlencoded' \
      --data-urlencode grant_type=refresh_token \
      --data-urlencode refresh_token=42min_rt_… \
      --data-urlencode client_id=42min_… \
      --data-urlencode client_secret=42min_cs_…
    

    You get a new access token and a new refresh token. Discard the old refresh token immediately — see reuse detection below.

    Refresh token reuse detection

    Refresh tokens are one-shot. Every successful refresh marks the presented refresh token as consumed and issues a new pair. If a previously-consumed (or revoked) refresh token is ever presented again, the entire grant family — every access and refresh token issued from the same authorization code — is revoked immediately. The caller gets:

    {
      "error": "invalid_grant",
      "message": "Refresh token has already been used; the session has been revoked"
    }
    

    This protects against the case where an attacker captures a refresh token: the moment the legitimate client or the attacker uses it a second time, the session is killed and the user must re-authorize.

    The same protection applies to authorization codes — replay of a consumed code revokes the whole grant family with error: invalid_grant, message: "Authorization code already used".

    Revocation

    curl -X POST https://api.42min.us/v1/oauth/revoke \
      -H 'Content-Type: application/x-www-form-urlencoded' \
      --data-urlencode token=42min_rt_… \
      --data-urlencode client_id=42min_… \
      --data-urlencode client_secret=42min_cs_…
    

    Per RFC 7009 the endpoint always returns 200 OK, even for unknown tokens. Revoking a refresh token also revokes every access token in the same grant family; revoking an access token revokes only that token.

    Introspection (confidential clients)

    curl -X POST https://api.42min.us/v1/oauth/introspect \
      -H 'Content-Type: application/x-www-form-urlencoded' \
      --data-urlencode token=42min_at_… \
      --data-urlencode client_id=42min_… \
      --data-urlencode client_secret=42min_cs_…
    

    Returns { "active": true, "scope": "…", "client_id": "…", "exp": …, … } for tokens owned by the calling client. Tokens belonging to a different client (or expired/revoked/unknown tokens) return { "active": false } — there is no information leak across clients.

    Client types and token-endpoint auth

    • Confidential clients authenticate with client_secret_basic or client_secret_post.
    • Public clients (mobile, SPAs) authenticate with PKCE only — token_endpoint_auth_method=none.

    Token lifetimes — quick reference

    Token TTL Rotated on refresh
    Authorization code 10 minutes — (single use)
    Access token 1 hour
    Refresh token 60 days yes
    PAT unlimited unless expiresAt is set

    Choosing PAT vs. OAuth

    • Building a personal script or an integration that acts as a specific user — PAT.
    • Building a product that other 42min users install to give your app access to their account — OAuth.

    PATs are simpler but inherit one user's identity. OAuth is more work but gives each end-user their own consent screen, their own scope set, and their own revocation control.

    Last updated May 14, 2026.