Skip to content

OAuth 2.0

Authenticate Wazapin integrations with Authorization Code + PKCE or the Device Authorization flow.

Wazapin supports OAuth 2.0 for integrations that need delegated access instead of a long-lived API key. Use OAuth for a CLI, desktop app, or external application that acts on behalf of a Wazapin user.

For server-to-server integrations that you control, API keys remain the simpler option.

OAuth server

Use https://api.wazapin.com as the issuer:

https://api.wazapin.com

Discover the current endpoints and supported capabilities programmatically:

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

The discovery document currently advertises:

  • Authorization Code with PKCE (S256)
  • Refresh tokens
  • Device Authorization Grant (RFC 8628)
  • Token introspection (RFC 7662)
  • Token revocation (RFC 7009)

Register an OAuth client

OAuth clients must be registered before they can request authorization. Registration provides a client_id, allowed redirect URI, grant types, response types, and scopes.

Do not put a client secret in a browser, desktop app, CLI, or other public client. Public clients should use PKCE. Confidential clients may use a secret only from a protected server.

The wazapin-docs client exists for the hosted documentation integration. Its registered callback URLs are deployment-specific; do not copy it for a new application. Ask Wazapin to register your own client and exact callback URL before using Authorization Code in production.

Device Authorization flow

Use Device Authorization for a CLI, SSH session, or another device that cannot safely open a callback URL. This is the recommended flow for headless integrations.

1. Request a device code

The device endpoint accepts JSON. client_id is required; scope is a space-delimited list.

curl -sS -X POST "https://api.wazapin.com/v1/oauth/device" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "client_id": "YOUR_CLIENT_ID",
    "scope": "messages:read messages:send channels:read"
  }'

A successful response contains:

{
  "device_code": "DEVICE_CODE",
  "user_code": "ABCD-EFGH",
  "verification_uri": "https://app.wazapin.id/v1/oauth/device",
  "verification_uri_complete": "https://app.wazapin.id/v1/oauth/device?user_code=ABCD-EFGH",
  "expires_in": 600,
  "interval": 5
}

The verification URL and expiry are returned by the server. Always use the returned values rather than hardcoding them.

2. Ask the user to authorize the device

Open verification_uri_complete in a browser. The user signs in, selects the Wazapin organizations the device may access, and authorizes the device.

Show the user the user_code as a fallback when the complete URL cannot be opened.

3. Poll for the token

Poll the token endpoint no more often than the returned interval. The token endpoint uses the OAuth-standard form-encoded body, not JSON.

curl -sS -X POST "https://api.wazapin.com/v1/oauth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "Accept: application/json" \
  --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
  --data-urlencode "device_code=DEVICE_CODE" \
  --data-urlencode "client_id=YOUR_CLIENT_ID"

While the user has not finished, the server returns an OAuth error such as:

{
  "error": "authorization_pending"
}

Stop polling when the code expires or an unrecoverable error is returned. After approval, the response contains a bearer token:

{
  "access_token": "wazapin_...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "messages:read messages:send channels:read"
}

4. Call the API

curl -sS "https://api.wazapin.com/v1/channels" \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Accept: application/json"

When a user authorizes more than one organization, send the organization identifier required by the target resource. Keep the access token in a secure local credential store; never print it in logs.

Authorization Code + PKCE

Use Authorization Code with PKCE for web, desktop, and mobile applications that can open a browser and receive a redirect.

The authorization endpoint is:

GET https://api.wazapin.com/v1/oauth/authorize

Generate a high-entropy code_verifier, derive its S256 code_challenge, and keep the verifier locally until the token exchange. Redirect the user to a URL like this:

https://api.wazapin.com/v1/oauth/authorize?
  response_type=code&
  client_id=YOUR_CLIENT_ID&
  redirect_uri=https%3A%2F%2Fyour-app.example%2Foauth%2Fcallback&
  scope=messages%3Aread%20messages%3Asend&
  state=RANDOM_STATE&
  code_challenge=BASE64URL_SHA256_CODE_VERIFIER&
  code_challenge_method=S256

Validate state when the browser returns to your callback. Then exchange the one-time authorization code:

curl -sS -X POST "https://api.wazapin.com/v1/oauth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "Accept: application/json" \
  --data-urlencode "grant_type=authorization_code" \
  --data-urlencode "client_id=YOUR_CLIENT_ID" \
  --data-urlencode "code=AUTHORIZATION_CODE" \
  --data-urlencode "redirect_uri=https://your-app.example/oauth/callback" \
  --data-urlencode "code_verifier=ORIGINAL_CODE_VERIFIER"

The redirect_uri must exactly match the URI registered for the client. The hosted docs domain is not a general-purpose OAuth callback; obtain a client registration for your own application before testing this flow.

Refresh an access token

When a refresh token is issued for your client, exchange it at the same token endpoint:

curl -sS -X POST "https://api.wazapin.com/v1/oauth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=refresh_token" \
  --data-urlencode "client_id=YOUR_CLIENT_ID" \
  --data-urlencode "refresh_token=REFRESH_TOKEN"

Treat refresh tokens as credentials. Store them securely and rotate or revoke them when a user disconnects the integration.

Introspect a token

Use introspection from a trusted backend to check whether a bearer token is active:

curl -sS -X POST "https://api.wazapin.com/v1/oauth/introspect" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "token": "ACCESS_TOKEN"
  }'

An active token returns active: true with token metadata. An unknown or expired token returns:

{
  "active": false
}

Do not expose introspection responses to untrusted clients.

Revoke a token

Revoke an access or refresh token when the user disconnects your integration:

curl -sS -X POST "https://api.wazapin.com/v1/oauth/revoke" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "token": "ACCESS_OR_REFRESH_TOKEN",
    "token_type_hint": "access_token"
  }'

The endpoint is idempotent for an already-revoked token. Discard the token locally after a successful revocation request.

OAuth errors

OAuth errors are returned as JSON from the token endpoint. Common cases include:

Error Meaning Client action
authorization_pending The device has not been approved yet. Wait for interval, then poll again.
expired_token The device code is no longer valid. Start a new device flow.
invalid_client The client ID or authentication is invalid. Check client registration.
invalid_grant The code, device code, or refresh token is invalid. Do not retry the same credential indefinitely.
invalid_request A required parameter is missing or malformed. Fix the request before retrying.
access_denied The user or authorization server denied access. Tell the user and stop the flow.

Security checklist

  • Use PKCE with S256 for public clients.
  • Validate state on every authorization callback.
  • Use exact, pre-registered redirect URIs; never accept arbitrary callback URLs.
  • Request the minimum scopes your integration needs.
  • Keep access and refresh tokens out of source control, URLs, logs, and client-side analytics.
  • Revoke tokens when the user disconnects the integration.

Next steps

Navigation

Type to search…

↑↓ navigate↵ selectEsc close