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.comDiscover the current endpoints and supported capabilities programmatically:
curl -sS https://api.wazapin.com/.well-known/oauth-authorization-serverThe 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/authorizeGenerate 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=S256Validate 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
S256for public clients. - Validate
stateon 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.