wats.sh

Capability status

Per-capability status of WATS against the WhatsApp Cloud API, tagged live-validated, shape-only, or planned.

active · reviewed 2026-06-21

Three tags. live-validated: exercised against live Meta Graph/webhooks. shape-only: implemented and tested against MockTransport and synthetic webhooks; Meta has not seen it. planned: not built.

CapabilitypywawatsStatus
Client constructionpywa.WhatsApp(...)GraphClient + WhatsApp facade; construction-time validation.live-validated
Scoped sub-clientsphone_id / WABA-bound methodsPhoneNumberClient, WABAClient; ids validated at construction.live-validated
Handler routingdecorator registrationTypedRouter: registration-order dispatch, handles with unregister(), observer hooks.live-validated
Filters& / | / ~ operator filtersfiltersTyped: and/or/not/custom plus message/status/template/group built-ins.shape-only — pure local predicates; nothing here talks to Meta.
Listenerslisten waiterswa.listen({ type, from?, filter?, timeoutMs?, signal? }) over a bounded registry.shape-only.
Sent-result waitersSentMessage.wait_for_reply etc.startChat(...) returns waitForReply, waitForClick, waitForSelection, waitForFlowCompletion, waitUntilDelivered, waitUntilRead, and waitUntilFailed, all resolved from observed webhooks only.shape-only — process-local listener registry, no persistence.
Webhook verificationserver configChallenge echo + constant-time X-Hub-Signature-256 HMAC over the raw body.live-validated
Webhook adapterbuilt-in serverRuntime-neutral WebhookAdapter with fetch / Bun / Node wrappers; identical status-code taxonomy.live-validated
Webhook normalizationupdate classesnormalizeWebhookEnvelopeTypedUpdate union; per-entry skip taxonomy, dedup, event cap.live-validated for message and status families; deeper families shape-only.
Update parsern/a (internal)Strict envelope validation, safety limits, malformed-skip counters.live-validated via the webhook path.
Error modelWhatsAppError subclassesGraphApiError hierarchy + registry seeded with pywa's 66 codes; instanceof narrowing.live-validated
Shared domain typesPython classesClosed discriminated unions for messages, statuses, contacts, webhook values.live-validated
Graph transportwa.apiTransport seam: createFetchTransport, createMockTransport, defineEndpoint registry.live-validated
Retry/backoffclient optionsOpt-in createReliableTransport: jittered backoff, Retry-After, no POST retries by default.shape-only.
Paginationiterator helperspaginate / paginateAll async generators over cursor pages.shape-only.
Crypto providern/a (internal)CryptoProvider seam with Node and WebCrypto adapters.live-validated via live HMAC verification.
Text sendssend_text / send_messagesendText, WhatsApp.startChat to arbitrary recipients.live-validated
Media sendssend_image etc.sendImage / sendVideo / sendAudio / sendDocument / sendSticker by media id or link.live-validated for image by media id; other media composers shape-only.
Other composerslocation, contacts, reactions, buttons, lists, CTA, catalog, mark-read, typingMatching helpers on PhoneNumberClient and the facade.shape-only.
One-call media sendfile/bytes polymorphismPhoneNumberClient.uploadAndSendImage / uploadAndSendVideo / uploadAndSendAudio / uploadAndSendDocument / uploadAndSendSticker upload in-memory Blob / ArrayBuffer / Uint8Array bodies, then send by returned media id. The Node/Bun-only @wats/graph/node-media subpath adds filesystem-path helpers.shape-only.
Media runtimemedia helpersuploadMedia, downloadMedia, downloadMediaBytes, deleteMedia, decryptEncryptedMedia, upload sessions.live-validated — upload/metadata/send/delete passed live; decrypt and sessions shape-only.
Template sendsend_templatesendTemplate with parameter-count validation; sendMarketingTemplate for marketing messages.live-validated — approved-template send produced the full sent/delivered/read chain; marketing send shape-only.
Template managementcreate_template etc.List/create/get/update/delete callables, component builders, template-group analytics, compareTemplates, unpauseTemplate, migrateTemplates, archive/unarchive helpers, auth-template upsert, and library-template create fields.live-validated for list reads; mutations/comparison/unpause/migration/archive/auth-upsert/library-template fields shape-only.
FlowsFlow managementList/get/create/update/publish/delete/deprecate/assets callables, bounded Flow JSON validation, typed FlowJSON DSL builders (screens/components/actions), metrics, migration, response builders.shape-only — live read returned empty; mutations/metrics/migration never exercised.
Flow encrypted data-channelricher Flow runtimedecryptRequest/encryptResponse (RSA-OAEP + AES-GCM, IV-flip response) and a framework-agnostic handleFlowRequest dispatch (ping/error-ack/close), backed by @wats/crypto.shape-only — local crypto round-trip; no hosted endpoint or live publish. Media-upload (CBC+HMAC) decryption is implemented shape-only.
Callinginitiate_call etc.Initiate/pre-accept/accept/reject/terminate callables, call-button message and deep-link helpers, typed calling webhooks and filters. See Calling Reference.shape-only — no live call sessions yet.
Call permissions + calling-settings mutationpermission models, SIP/settings writesgetCallPermissions (typed permission/action/limit models), calling settings sub-object on updatePhoneNumberSettings (status, call hours, call icons, callback permission, SIP servers), call_permission_reply webhook fields (isPermanent, responseSource, plus fromUserId/fromParentUserId).shape-only — no live calls; WebRTC/media orchestration still planned.
Business/admin readsget_business_account etc.getWabaInfo, listSubscribedApps, listPhoneNumbers, getPhoneNumberInfo, getBusinessProfile, getCommerceSettings.live-validated
Phone-number settings readsettings gettergetPhoneNumberSettings (storage config, optional SIP credentials as sipUserPassword) plus getWabaInfo calling health as healthStatus.canReceiveCallSip.shape-only — not exercised in the live campaign.
Business/admin mutationsprofile/commerce/settings updatesupdateBusinessProfile, updateCommerceSettings, updatePhoneNumberSettings, Block API, OBA/display-name helpers, the phone registration lifecycle (createPhoneNumber, requestVerificationCode, verifyPhoneNumber, registerPhoneNumber, deregisterPhoneNumber, setTwoStepVerificationPin), business public-key helpers (getBusinessPublicKey, setBusinessPublicKey), and QR code CRUD (createQrCode, listQrCodes, getQrCode, updateQrCode, deleteQrCode).shape-only.
Admin: tokens, catalog CRUDestablishedEmbedded Signup token exchange is shape-only. Catalog/product inventory CRUD is not built.planned for catalog CRUD.
Groupsno pywa equivalentTypes, endpoint callables, GroupClient, webhook normalization, filters, facade helpers, opt-in service routes.shape-only — live listGroups passed; createGroup blocked by a Meta entitlement on the test number, so the mutation matrix is unproven.
Configconstructor args@wats/config: versioned YAML/JSON profiles with env-secret references, never raw secrets.shape-only — local-only by design.
CLIn/awats init/setup/doctor/openapi/serve/onboarding/upgrade; credential-safe by default.shape-only — local tooling; live serve is the operator's opt-in.
Servicen/acreateWatsServiceApp: health/readiness, webhook delegation, authenticated message routes, Graph-failure diagnostics.live-validated for webhook ingress and text send; other routes shape-only.
Message event-store projection (outbound)n/a@wats/persistence wats_messages + wats_message_status_events tables and recordMessage / appendMessageStatus / getMessage / listMessages APIs; read-only GET /messages and GET /messages/{messageId} service routes; wats messages list/show CLI inspection; Postgres adapter at @wats/persistence/postgres (shape-only, mock-client tested).shape-only — local SQLite + MockTransport only; Postgres mock-client tested; no live Meta calls this slice.
OpenAPI documentn/aOpenAPI 3.1 for WATS service routes, served at /openapi.json.shape-only.
Internal utilitiesn/a@wats/internal-utils shared helpers.shape-only — never leaves the process.
OpenTelemetry observabilityn/aNot built; router/adapter observer hooks exist as seams.planned.

Live-validation run log: campaign log.

Notes

  • Webhook media ids are downloadable for 7 days after receipt (Meta reduced the window from 30 days to 7, effective 2025-10-09). Download promptly and persist to your own storage if you need media later; WATS does not persist it for you.
  • A send accepted with HTTP 200 proves Meta took the request, not that anyone received it. Delivered/read state comes only from observed status webhooks.

On this page