# WhatsOS OTP API integration context for coding assistants

Build a server-side phone verification integration using the existing WhatsOS API.
API base: https://whatsos.com/api/v1/otp
OpenAPI: https://whatsos.com/otp/openapi.json
Guide: https://whatsos.com/otp/guide.html
Integration downloads: https://whatsos.com/otp/integrations.html

Do not invent endpoints, scopes or pricing. Read OpenAPI before writing code.
Keep WHATSOS_OTP_KEY in server-side environment/configuration only. Never include it in
React/Next.js client bundles, NEXT_PUBLIC_* variables, mobile apps, prompts or logs.
Use separate wotp_test_ and wotp_live_ project keys. Test projects simulate delivery with
code 123456. Test results must not authenticate users or approve production actions.

API contract:
- POST /verifications {to, purpose, locale?, reference?}: requests a code (202).
- POST /verifications/{id}/check {code}: check user input (200).
- POST /verifications/{id}/resend {}: replaces a challenge after cooldown (202).
- POST /verifications/{id}/cancel {}: cancel an active challenge (200).
- GET /verifications/{id}: read masked state (200).
All requests: Authorization: Bearer SERVER_API_KEY; JSON over HTTPS.
Every POST: Idempotency-Key with 8-128 characters [A-Za-z0-9_.:-].
Use otp:send for send/resend; otp:verify for check; otp:read for GET; otp:cancel for cancel.
Phone: E.164 (+countrycode and number). Code: exactly six digits, stored as a string.
Purpose must be enabled in the project (defaults: login, signup, phone_verification).
Locale must match the project's configured authentication template language.

Implement an application-owned server session for each verification attempt. Store the
verification_id, intended phone, purpose and environment against that session. Never accept
an arbitrary verification_id from a browser as authorization to complete someone else's action.
Do not log API keys, OTP codes or full phone numbers. Mask user-facing phone numbers.

Generate a new idempotency key for each logical send/check/resend/cancel. Save it before the
request. On a timeout or ambiguous network failure, retry the SAME payload and SAME key.
Do not retry a different code or payload under the old key. A definite rate-limit rejection
requires waiting before starting a new operation. Honor Retry-After and resend_after.
A resend returns a NEW verification_id; replace the old server-side binding.

Approve the bound action only when check returns verified === true, in the intended live
project and session. Sending, message delivery and HTTP 200 alone do not mean verified.
Consume your application's verification proof atomically once; upstream idempotency replay
may return an earlier true result. Expire local attempts and clear them on logout/cancellation.
Protect public application endpoints with CSRF/session binding and per-IP/per-phone abuse
limits. Add a CAPTCHA or equivalent abuse control appropriate to the application.
Handle incorrect code, expired, locked, cancelled, superseded, invalid key, quota/setup,
429 and 503 states. Never fail open during outages.

For signed webhooks, validate the original bytes, timestamp and HMAC according to the guide;
reject stale signatures and deduplicate event IDs. Do not use unverified webhooks for login.
Test with mocks or test projects only. Do not send real OTPs or create paid traffic by default.

WordPress: download the official WhatsOS plugin from the integration page. Use [whatsos_otp]
in a Shortcode block/widget. For a protected server action call
whatsos_otp_consume($expected_phone, 'phone_verification'); reject WP_Error results.
This plugin provides phone verification, not automatic WordPress/WooCommerce login.
The browser's whatsos:verified event is UI feedback, never proof for a protected action.
