ZetPinDevelopers

OAuth 2.0 flow

ZetPin uses the OAuth 2.0 authorization-code grant. Redirect the user to consent, receive a single-use code, and exchange it for scoped tokens.

Endpoints

PurposeMethodURL
Authorize (user consent)GEThttps://api.zetpin.com/oauth/authorize
Token exchange & refreshPOSThttps://api.zetpin.com/oauth/token
Revoke a tokenPOSThttps://api.zetpin.com/oauth/revoke

The authorization-code flow

  1. Your app redirects the user to /oauth/authorize with your client_id, redirect_uri, response_type=code, the requested scope, and a random state. Public clients also send a PKCE code_challenge.
  2. The user signs in with a phone OTP on ZetPin's consent page and approves the requested scopes.
  3. ZetPin redirects back to your redirect_uri with a single-use code (prefix zpac_, 5-minute TTL) and your original state.
  4. Your server POSTs the code to /oauth/token with grant_type=authorization_code to receive a zpat_ access token and zprt_ refresh token.
  5. You call /dev/v1 endpoints with the access token as a Bearer credential, and refresh before it expires.

Confidential vs public clients

How your app authenticates at the token endpoint depends on whether it can keep a secret.

Client typeWhere it runsToken-endpoint auth
ConfidentialServer-side (backend you control)client_secret — PKCE optional but recommended
PublicMobile apps, SPAs, anything shipped to usersPKCE code_verifiermandatory, no secret

PKCE is mandatory for public clients

Public clients cannot hold a client_secret safely, so they must use PKCE (S256) to bind the authorization code to the app that requested it. See PKCE for how to generate the verifier and challenge.

redirect_uri must match exactly

The redirect_uri you send on /oauth/authorize and again on /oauth/token must be a byte-for-byte match of a value registered for your app — including scheme, host, port, path, and any trailing slash. A mismatch is rejected before consent. Register every callback URL you intend to use.

Use state for CSRF protection

Generate an unguessable state value per authorization request, store it in the user's session, and send it on /oauth/authorize. ZetPin echoes it back on the redirect. Reject the callback if the returned state doesn't match what you stored — this prevents cross-site request forgery on your callback.

Example: authorize request

http
GET https://api.zetpin.com/oauth/authorize
  ?client_id=zp_app_xxx
  &redirect_uri=https://yourapp.com/callback
  &response_type=code
  &scope=profile.read posts.write posts.read
  &state=8f3c9a2e
  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256

Example: redirect back to your app

http
# On approval
GET https://yourapp.com/callback?code=zpac_a1b2c3d4...&state=8f3c9a2e

# If the user declines consent
GET https://yourapp.com/callback?error=access_denied&state=8f3c9a2e

Next

Continue to PKCE to build your challenge, or jump to Refreshing & revoking for the token lifecycle.