feat(channels): align whatsapp calls api
This commit is contained in:
@@ -49,16 +49,16 @@ Hermes task landing checklist:
|
||||
|
||||
## Current Baseline
|
||||
|
||||
- Current tracking checkpoint: 2026-06-06 account onboarding route/payload parity checkpoint, prepared as `feat(accounts): align onboarding update`.
|
||||
- Latest implementation checkpoint: this checkpoint, prepared as `feat(accounts): align onboarding update`.
|
||||
- Latest documentation/tooling checkpoint: this tracker update for P3.36 plus the landed parity tracker history; this document is the active follow-up plan and supersedes `.hermes/plans/*`.
|
||||
- Current tracking checkpoint: 2026-06-06 account WhatsApp calls API parity checkpoint, prepared as `feat(channels): align whatsapp calls api`.
|
||||
- Latest implementation checkpoint: this checkpoint, prepared as `feat(channels): align whatsapp calls api`.
|
||||
- Latest documentation/tooling checkpoint: this tracker update for P3.37 plus the landed parity tracker history; this document is the active follow-up plan and supersedes `.hermes/plans/*`.
|
||||
- Plan landing status: complete for the current known Hermes plans and user-confirmed scope. Future work should update this file directly instead of opening a parallel tracker.
|
||||
- Worktree status at this implementation checkpoint: account onboarding update from `reference/chatwoot/config/routes.rb:58`, `app/controllers/api/v1/accounts/onboardings_controller.rb`, account update/model Jbuilder views, and reused dashboard onboarding/store clients is implemented. GoChat now registers administrator-gated `PATCH /api/v1/accounts/:account_id/onboarding`, accepts the frontend raw onboarding payload, persists permitted account fields plus account `custom_attributes`, clears `onboarding_step` when finalizing account details, and returns the raw Chatwoot account serializer shape. Account notification scoping, API v2 year-in-review, and WhatsApp calling toggle parity remain implemented from previous checkpoints. Live API/browser/enterprise smoke still needs the full PostgreSQL/Redis/Meilisearch/GoChat/Vite/Chrome stack.
|
||||
- Worktree status at this implementation checkpoint: account WhatsApp calls API from `reference/chatwoot/config/routes.rb:237-245`, enterprise `WhatsappCallsController`, WhatsApp call Jbuilder views/specs, `Whatsapp::CallService`, `Call`, and reused dashboard `whatsappCallsAPI.js` is implemented. GoChat now registers the account-level show/initiate/accept/reject/terminate/upload-recording routes used by the frontend, persists WhatsApp calls in the Chatwoot `calls` model boundary, creates linked `voice_call` messages for outbound calls, updates call/message/conversation status for accept/reject/terminate, handles permission-request and recording idempotency payloads, and keeps the old conversation-nested tracking routes as compatibility aliases. Account onboarding, notification scoping, API v2 year-in-review, and WhatsApp calling toggle parity remain implemented from previous checkpoints. Live API/browser/enterprise smoke still needs the full PostgreSQL/Redis/Meilisearch/GoChat/Vite/Chrome stack.
|
||||
- Next executable implementation checkpoint: continue Phase 2/3 drift audit, Phase 6 placeholder audit, or B12 live smoke from fresh reference/smoke evidence.
|
||||
- `go test ./...` passes when run outside the restricted socket sandbox; focused Notification handler/repository/service tests pass in the sandbox.
|
||||
- Route dump succeeds with `952` registered routes after account onboarding route tracking.
|
||||
- `go test ./...` passes when run outside the restricted socket sandbox; focused WhatsApp call service/handler/router/route-parity tests pass in the sandbox.
|
||||
- Route dump succeeds with `958` registered routes after account WhatsApp calls route tracking.
|
||||
- Route parity artifacts now exist under `docs/parity/` and are generated by `cmd/route_parity`.
|
||||
- Tracked frontend-critical route audit covers 415 Chatwoot routes: 402 exact, 0 method-compatible, 13 parameter-compatible, 0 missing. The 13 parameter-compatible routes are Gin-internal parameter-name differences for nested AgentCapacityPolicy users/inbox limits, dashboard app member `:id` names, plus the public article `.md`/`.png` suffixes served through the same external article route dispatcher.
|
||||
- Tracked frontend-critical route audit covers 421 Chatwoot routes: 403 exact, 0 method-compatible, 18 parameter-compatible, 0 missing. The 18 parameter-compatible routes are Gin-internal parameter-name differences for nested AgentCapacityPolicy users/inbox limits, dashboard app member `:id` names, WhatsApp call `:id` route names, plus the public article `.md`/`.png` suffixes served through the same external article route dispatcher.
|
||||
- `/api/v1/widget` stubs are burned down and public inbox/contact/conversation/message core flows are backed by real handlers.
|
||||
- Handler test stability fixes are committed into the baseline before feature parity work continues.
|
||||
- `.codegraph/` is generated indexing output and is not part of tracked product code.
|
||||
@@ -140,6 +140,7 @@ This table is the shortest authoritative handoff view. If an older lower section
|
||||
|
||||
| Priority | Workstream | Current state | Next checkpoint | Commit close rule |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 0 | P3.37 account WhatsApp calls API parity | Implemented for reused dashboard WhatsApp WebRTC call controls: account-level `GET /api/v1/accounts/:account_id/whatsapp_calls/:id`, `POST /whatsapp_calls/initiate`, `POST /whatsapp_calls/:id/accept`, `reject`, `terminate`, and `upload_recording` are registered and tracked from `routes.rb:237-245`; initiate validates enabled WhatsApp Cloud calling, `sdp_offer`, and contact phone; outbound calls persist as Chatwoot `calls` with linked `voice_call` messages and return `{ status: "calling", call_id }`; accept/reject/terminate update call/message/conversation status through a fakeable Meta provider boundary; permission-request and upload-recording idempotency responses match the inspected controller/spec contract. | Keep in Review; reopen only if live WhatsApp calling smoke exposes Meta call action body drift, ActionCable broadcast requirements beyond persisted state, permission-template throttling drift, recording storage URL drift, or call bubble serializer drift beyond the inspected enterprise controller/service/Jbuilder/frontend contract. | Focused WhatsApp call service/handler tests, router/route-parity tests, route dump/parity regeneration (`958` routes; `403 exact`, `18 parameter-compatible`, `0 missing out of 421`), full `go test ./...`, and `git diff --check` must pass. |
|
||||
| 0 | P3.36 account onboarding route/payload parity | Implemented for reused dashboard onboarding: `PATCH /api/v1/accounts/:account_id/onboarding` is registered and tracked from `routes.rb:58`, administrator-gated like Chatwoot `OnboardingsController#update`, accepts raw `name`, `locale`, `website`, `industry`, `company_size`, `timezone`, `referral_source`, and `user_role`, persists account `custom_attributes` through `accounts.custom_attributes`, clears `onboarding_step` when current account details are finalized, and returns the raw account serializer with selected custom-attribute fields. | Keep in Review; reopen only if live onboarding smoke exposes additional account serializer fields, empty-string/null strong-param drift, admin policy drift, or onboarding step behavior beyond the inspected controller/Jbuilder/frontend contract. | Focused Account handler/service tests, router/route-parity tests, route dump/parity regeneration (`952` routes; `402 exact`, `13 parameter-compatible`, `0 missing out of 415`), full `go test ./...`, and `git diff --check` must pass. |
|
||||
| 0 | P3.35 account notification scoping hardening | Implemented for reused dashboard notification actions: account notifications now follow Chatwoot `fetch_notification` scope (`current_user.notifications.where(account_id: Current.account.id)`) for show, update/read, destroy, snooze, unread, and unread count. `snoozed_until` is parsed as Unix seconds like `DateRangeHelper`, snooze merges `meta.last_snoozed_at: null`, missing `snoozed_until` returns the scoped notification without mutation, destroy returns empty `200 OK`, and cross-user/cross-account mutations return `404`. | Keep in Review; reopen only if live notification-center smoke exposes list metadata, snooze timestamp, read/unread, destroy_all async, or notification serializer drift beyond the inspected controller/finder/Jbuilder/frontend contract. | Focused Notification handler/repository/service tests, full `go test ./...`, and `git diff --check` passed. No route artifacts change because only handler/repository/service behavior changed. |
|
||||
| 0 | P3.34 API v2 year-in-review parity | Implemented for reused dashboard year-in-review client: `GET /api/v2/accounts/:account_id/year_in_review?year=YYYY` is registered and tracked from `routes.rb:505`, returns raw `{ year, total_conversations, busiest_day, support_personality }`, defaults missing `year` to `2025`, reads/writes the current user's `ui_settings` cache under `year_in_review_<account_id>_<year>`, counts conversations assigned to the current user during the selected year, formats the busiest assigned conversation day as `Mon DD`, and truncates the average `first_response` reporting-event value into seconds. | Keep in Review; reopen only if live reused-dashboard year-in-review smoke exposes timezone, cache-present, invalid-year, or payload-shape drift beyond the inspected controller/builder/frontend contract. | Focused YearInReview service/handler tests, router/route-parity tests, route dump/parity regeneration (`951` routes; `401 exact`, `13 parameter-compatible`, `0 missing out of 414`), full `go test ./...`, and `git diff --check` passed. |
|
||||
@@ -155,7 +156,7 @@ This table is the shortest authoritative handoff view. If an older lower section
|
||||
| 0 | P3.24 Slack integration parity | Implemented for reused dashboard Slack settings flow: no-trailing and trailing singleton routes are registered for create/update/delete, `PUT` and `PATCH` update both work, create accepts frontend `code` and exchanges it for a Slack access token, hooks are persisted with `app_id: slack` and disabled status, update accepts frontend `reference_id`, fetches real private/public Slack channels with pagination, joins public channels, persists `reference_id/settings.channel_name/status`, create/update return raw Chatwoot app payloads with hooks, list-all returns raw channel arrays, invalid channels return Chatwoot's `422 { error }`, and delete returns empty `200 OK`. | Keep in Review; reopen only if live Slack OAuth/channel smoke exposes OAuth redirect, provider error, app serializer, or Slack channel pagination drift beyond the inspected controller/builder/spec/frontend contract. | Focused Slack handler/service tests, route dump/parity regeneration, sandbox focused `go test`, escalated full `go test ./...`, and `git diff --check` passed. |
|
||||
| 0 | P3.23 nested contact inbox creation API | Implemented for Chatwoot nested contact inbox creation: raw JSON/form/query params are accepted, contact and inbox are account-scoped, missing source IDs are generated through Chatwoot channel rules, duplicate contact+inbox+source rows are returned idempotently, `hmac_verified` is persisted on creation, and the response is raw `{ source_id, inbox }` rather than the local model/envelope. | Keep in Review; reopen only if live CRM/new-conversation smoke exposes inbox access-policy, unsupported channel, or serializer drift beyond the inspected controller/builder/Jbuilder contract. | Focused nested ContactInbox handler/service/repository tests, route parity check, full `go test ./...`, and `git diff --check` passed. |
|
||||
| 1 | P3.2a invitation/confirmation mail parity | Implemented for current non-SSO reference behavior: profile resend is no longer a TODO-only log, new invited agents and unconfirmed invited profile resends generate reset-password invitation links, normal unconfirmed profile resends generate confirmation links, `users.unconfirmed_email` is modeled for email-update routing, and fakeable/environment SMTP mailers keep default tests offline. | Keep in Review; reopen only if reused frontend smoke or fresh reference evidence exposes additional Devise confirmation states outside excluded SSO/SAML/LDAP/OIDC variants. | Focused profile and agent invitation tests, combined handler/service/repository/router/migrate/app tests, full `go test ./...`, and `git diff --check` passed. |
|
||||
| 2 | Phase 2/3 drift | Tracked route parity is 0 missing for the current 414-route critical set; dashboard `/app` shell routes from `routes.rb:19-20`, `.well-known` app association and custom-domain challenge routes from `routes.rb:657-660`, Twilio callback routes from `routes.rb:639-640`, enterprise Twilio voice routes from `routes.rb:643-646`, root Linear/Shopify/Notion OAuth callback routes from `routes.rb:630/634/654`, root Twitter/Google/Microsoft/Instagram/TikTok callback routes from `routes.rb:626/649-652`, assignment policy routes from `routes.rb:306-313`, help-center portal/category/article routes from `routes.rb:385-404`, public help-center portal/sitemap/article/category/search/article-detail routes from `routes.rb:590-601`, enterprise contact outbound voice call from `routes.rb:216`, account agent-bot routes from `routes.rb:94-97`, account webhook routes from `routes.rb:342`, account integration app/hook routes from `routes.rb:345-348`, account Slack routes from `routes.rb:350-352`, account Dyte routes from `routes.rb:357-358`, account Shopify routes from `routes.rb:361-364`, account Linear routes from `routes.rb:365-373`, account Notion authorization/destroy routes from `routes.rb:335/379`, account Twitter/Google/Microsoft/Instagram/TikTok authorization routes from `routes.rb:315/319/323/327/331`, account WhatsApp authorization route from `routes.rb:339`, account inbox WhatsApp calling toggle routes from `routes.rb:266-267`, API v2 year-in-review route from `routes.rb:505`, dashboard app routes from `routes.rb:130`, canned response routes from `routes.rb:114`, notification subscription routes from `routes.rb:440`, team/team-member routes from `routes.rb:296-300`, conversation participant routes from `routes.rb:150`, conversation direct upload route from `routes.rb:151`, conversation draft message routes from `routes.rb:152`, conversation inbox assistant route from `routes.rb:165`, conversation reporting events route from `routes.rb:166`, and account reporting events route from `routes.rb:234` are now explicitly tracked. Notification list/action serializers plus current-user/current-account mutation scoping, user notification-settings raw payloads, campaigns raw payload/display-id routes, Devise password reset/confirmation payloads, CRM shared attachment payloads plus fixed 100-row attachment pagination, account/settings payloads, assignable-agent payloads, agent index full-list behavior, agent create/update/delete defaults/errors/scope, account agent-bot route/payload/mutation behavior, account webhook payload/mutation behavior, integration app/hook payload behavior, account Slack OAuth/channel payload behavior, account Dyte create/join payload behavior, account Shopify customer-order payload behavior, account Linear GraphQL issue payload behavior, account Notion authorization/destroy behavior, account Twitter/Google/Microsoft/Instagram/TikTok authorization behavior, account WhatsApp embedded-signup creation and reauthorization behavior, account inbox WhatsApp calling toggle behavior, API v2 year-in-review raw/cache/aggregate behavior, dashboard app raw payload/serializer behavior, canned response raw payload/search/delete behavior, notification subscription payload behavior, team update/frontend route behavior, conversation participant route/payload/final-set update behavior, conversation direct upload ActiveStorage behavior, conversation draft message Redis-key-equivalent behavior, conversation inbox assistant Copilot payload behavior, conversation reporting-event raw array behavior, account reporting-events payload/filter/pagination behavior, label CRUD payloads, custom filters, custom attribute definitions, contact outbound voice calls, assignment policy CRUD and inbox binding payloads, Twilio inbound/status callbacks, enterprise Twilio voice callbacks, Linear/Shopify/Notion root integration callbacks, Shopify OAuth auth redirects, root channel OAuth callbacks, help-center portal/category/article payloads, dashboard app shell route behavior, app association JSON payloads, Cloudflare custom hostname verification, public widget popular-article lists, public help-center category list/show payloads, public portal show/default-locale payloads, public portal search payloads, public article show/markdown/tracking routes, and public sitemap XML now match the inspected Chatwoot contract. | Continue the next evidence-backed route/controller/serializer drift after P3.35 notification scoping hardening or from B12 findings. | Regenerate parity artifacts when routes change and add endpoint-family fixture tests. |
|
||||
| 2 | Phase 2/3 drift | Tracked route parity is 0 missing for the current 421-route critical set; dashboard `/app` shell routes from `routes.rb:19-20`, `.well-known` app association and custom-domain challenge routes from `routes.rb:657-660`, Twilio callback routes from `routes.rb:639-640`, enterprise Twilio voice routes from `routes.rb:643-646`, root Linear/Shopify/Notion OAuth callback routes from `routes.rb:630/634/654`, root Twitter/Google/Microsoft/Instagram/TikTok callback routes from `routes.rb:626/649-652`, assignment policy routes from `routes.rb:306-313`, help-center portal/category/article routes from `routes.rb:385-404`, public help-center portal/sitemap/article/category/search/article-detail routes from `routes.rb:590-601`, enterprise contact outbound voice call from `routes.rb:216`, account agent-bot routes from `routes.rb:94-97`, account webhook routes from `routes.rb:342`, account integration app/hook routes from `routes.rb:345-348`, account Slack routes from `routes.rb:350-352`, account Dyte routes from `routes.rb:357-358`, account Shopify routes from `routes.rb:361-364`, account Linear routes from `routes.rb:365-373`, account Notion authorization/destroy routes from `routes.rb:335/379`, account Twitter/Google/Microsoft/Instagram/TikTok authorization routes from `routes.rb:315/319/323/327/331`, account WhatsApp authorization route from `routes.rb:339`, account inbox WhatsApp calling toggle routes from `routes.rb:266-267`, account WhatsApp call routes from `routes.rb:237-245`, account onboarding route from `routes.rb:58`, API v2 year-in-review route from `routes.rb:505`, dashboard app routes from `routes.rb:130`, canned response routes from `routes.rb:114`, notification subscription routes from `routes.rb:440`, team/team-member routes from `routes.rb:296-300`, conversation participant routes from `routes.rb:150`, conversation direct upload route from `routes.rb:151`, conversation draft message routes from `routes.rb:152`, conversation inbox assistant route from `routes.rb:165`, conversation reporting events route from `routes.rb:166`, and account reporting events route from `routes.rb:234` are now explicitly tracked. Notification list/action serializers plus current-user/current-account mutation scoping, user notification-settings raw payloads, campaigns raw payload/display-id routes, Devise password reset/confirmation payloads, CRM shared attachment payloads plus fixed 100-row attachment pagination, account/settings payloads, account onboarding custom-attribute payloads, assignable-agent payloads, agent index full-list behavior, agent create/update/delete defaults/errors/scope, account agent-bot route/payload/mutation behavior, account webhook payload/mutation behavior, integration app/hook payload behavior, account Slack OAuth/channel payload behavior, account Dyte create/join payload behavior, account Shopify customer-order payload behavior, account Linear GraphQL issue payload behavior, account Notion authorization/destroy behavior, account Twitter/Google/Microsoft/Instagram/TikTok authorization behavior, account WhatsApp embedded-signup creation and reauthorization behavior, account inbox WhatsApp calling toggle behavior, account WhatsApp call lifecycle/recording behavior, API v2 year-in-review raw/cache/aggregate behavior, dashboard app raw payload/serializer behavior, canned response raw payload/search/delete behavior, notification subscription payload behavior, team update/frontend route behavior, conversation participant route/payload/final-set update behavior, conversation direct upload ActiveStorage behavior, conversation draft message Redis-key-equivalent behavior, conversation inbox assistant Copilot payload behavior, conversation reporting-event raw array behavior, account reporting-events payload/filter/pagination behavior, label CRUD payloads, custom filters, custom attribute definitions, contact outbound voice calls, assignment policy CRUD and inbox binding payloads, Twilio inbound/status callbacks, enterprise Twilio voice callbacks, Linear/Shopify/Notion root integration callbacks, Shopify OAuth auth redirects, root channel OAuth callbacks, help-center portal/category/article payloads, dashboard app shell route behavior, app association JSON payloads, Cloudflare custom hostname verification, public widget popular-article lists, public help-center category list/show payloads, public portal show/default-locale payloads, public portal search payloads, public article show/markdown/tracking routes, and public sitemap XML now match the inspected Chatwoot contract. | Continue the next evidence-backed route/controller/serializer drift after P3.37 WhatsApp calls or from B12 findings. | Regenerate parity artifacts when routes change and add endpoint-family fixture tests. |
|
||||
| 3 | Phase 6 placeholder audit | Widget/public/webhook critical placeholders are burned down; inbox WhatsApp health/register-webhook and sync-template drift are closed; refreshed `docs/parity/placeholder_audit.md` shows only webhook nil-handler fallbacks still call `chatwootParityStub`; dashboard conversation transcript/custom-attribute response drift and message retry status drift are closed. | Keep in Review; reopen only if fresh `rg`, route smoke, or B12 finds a frontend-reachable placeholder/stub in account/contact/conversation/message/inbox/widget/public paths. | `rg` placeholder audit and `scripts/parity_frontend_smoke.sh --check` are recorded; no reused-frontend blocker is ownerless. |
|
||||
| 4 | P3.9 account agent-bot API | Implemented for the reused dashboard AgentBots settings route with no-trailing-slash routes, PATCH update, raw Jbuilder-style payloads, account mutation scope, system-bot show/list visibility, empty `200 OK` delete, and full reset/avatar action payloads. | Keep in Review; reopen only if live settings smoke exposes avatar upload storage or administrator-secret gating drift. | Focused AgentBot handler tests, service/router focused tests, route dump/parity regeneration, full `go test ./...`, and `git diff --check` passed. |
|
||||
| 5 | P3.10 account webhooks API | Implemented for the reused dashboard Webhooks settings route with PATCH update, Chatwoot `{ payload }` list/mutation serializers, nested `{ webhook: ... }` bodies, generated secret, account-scoped mutations, URL/subscription validation, optional inbox serialization, and empty `200 OK` delete. | Keep in Review; reopen only if live settings smoke exposes audit writer or delivery-signature drift beyond the existing delivery service boundary. | Focused webhook handler/service/router tests, migration test, route dump/parity regeneration, full `go test ./...`, and `git diff --check` passed. |
|
||||
@@ -182,6 +183,7 @@ These rows are the executable development plan from this point forward. A checkp
|
||||
|
||||
| ID | Owner files | Reference files | Work to land | Exit gate |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| P3.37 account WhatsApp calls API parity | `internal/model/call.go`, `internal/service/whatsapp_call_service.go`, `internal/handler/api/v1/whatsapp_call_handler.go`, `internal/router/router.go`, `cmd/route_parity/main.go`, `migrations/000034_align_calls_whatsapp_fields.*`, WhatsApp call service/handler/router tests | `reference/chatwoot/config/routes.rb:237-245`, `reference/chatwoot/enterprise/app/controllers/api/v1/accounts/whatsapp_calls_controller.rb`, `reference/chatwoot/enterprise/app/views/api/v1/models/_whatsapp_call.json.jbuilder`, `reference/chatwoot/enterprise/app/views/api/v1/accounts/whatsapp_calls/*.json.jbuilder`, `reference/chatwoot/enterprise/app/services/whatsapp/call_service.rb`, `reference/chatwoot/enterprise/app/services/voice/call_message_builder.rb`, `reference/chatwoot/enterprise/app/models/call.rb`, `reference/chatwoot/spec/enterprise/controllers/api/v1/accounts/whatsapp_calls_controller_spec.rb`, `reference/chatwoot/app/javascript/dashboard/api/channel/whatsapp/whatsappCallsAPI.js` | Done. Account WhatsApp calls now match the reused dashboard route family: account-level show/initiate/accept/reject/terminate/upload-recording routes are tracked and registered; initiate resolves conversations by display ID, validates WhatsApp Cloud calling and contact phone, calls a fakeable Meta provider boundary, creates outbound `calls` rows and linked `voice_call` messages, and returns `{ status, call_id }`; accept/reject/terminate update call/message/conversation status; permission requests record conversation attributes; recording upload is idempotent over audio attachments. | Review by `feat(channels): align whatsapp calls api`; focused service tests cover outbound creation, permission request, accept/terminate/recording, guard errors, and no-answer termination; handler tests cover account route payloads; route artifacts cover the six enterprise routes; full `go test ./...` and `git diff --check` must pass. |
|
||||
| P3.36 account onboarding route/payload parity | `internal/model/account.go`, `internal/service/account_service.go`, `internal/handler/api/v1/account_handler.go`, `internal/router/router.go`, `cmd/route_parity/main.go`, `migrations/000033_add_accounts_custom_attributes.*`, account handler/service/router tests | `reference/chatwoot/config/routes.rb:58`, `reference/chatwoot/app/controllers/api/v1/accounts/onboardings_controller.rb`, `reference/chatwoot/app/views/api/v1/accounts/update.json.jbuilder`, `reference/chatwoot/app/views/api/v1/models/_account.json.jbuilder`, `reference/chatwoot/app/javascript/dashboard/api/onboarding.js`, `reference/chatwoot/app/javascript/dashboard/store/modules/accounts.js`, `reference/chatwoot/app/javascript/dashboard/routes/dashboard/onboarding/Index.vue` | Done. Account onboarding update now matches the reused dashboard path: `PATCH /api/v1/accounts/:account_id/onboarding` is tracked and registered with administrator authorization; the handler accepts the raw frontend onboarding payload; the service merges permitted onboarding attributes into persisted account `custom_attributes`, mirrors timezone for existing account consumers, clears `onboarding_step` only when finalizing account details, and the account serializer emits the Chatwoot selected custom-attribute fields while preserving subscription/deletion placeholders. | Review by `feat(accounts): align onboarding update`; focused account handler tests cover finalizing and non-finalizing onboarding merges plus raw account response shape; service/router/route-parity tests and route artifacts cover the new account route; full `go test ./...` and `git diff --check` must pass. |
|
||||
| P3.35 account notification scoping hardening | `internal/repository/notification_repo.go`, `internal/service/notification_service.go`, `internal/handler/api/v1/notification_handler.go`, notification handler/repository/service tests | `reference/chatwoot/config/routes.rb:283-291`, `reference/chatwoot/app/controllers/api/v1/accounts/notifications_controller.rb`, `reference/chatwoot/app/finders/notification_finder.rb`, `reference/chatwoot/app/views/api/v1/accounts/notifications/index.json.jbuilder`, `reference/chatwoot/app/javascript/dashboard/api/notifications.js` | Done. Account notification show/update/destroy/snooze/unread/unread_count now use current-user/current-account scope; cross-account and cross-user rows do not leak; `snoozed_until` accepts Unix seconds; snooze merges `meta.last_snoozed_at`; absent `snoozed_until` is a scoped no-op; destroy keeps Chatwoot empty `200 OK`. | Review by `feat(notifications): tighten chatwoot scoping`; focused handler tests cover mutation scoping, account unread count, Unix snooze, no-body snooze, and destroy route coverage; repository/service notification tests cover scoped helpers; full `go test ./...` and `git diff --check` passed. No route artifacts change. |
|
||||
| P3.34 API v2 year-in-review parity | `internal/service/year_in_review_service.go`, `internal/handler/api/v1/year_in_review_handler.go`, `internal/router/router.go`, `internal/app/bootstrap.go`, `cmd/route_parity/main.go`, YearInReview service/handler/router tests | `reference/chatwoot/config/routes.rb:505`, `reference/chatwoot/app/controllers/api/v2/accounts/year_in_reviews_controller.rb`, `reference/chatwoot/app/builders/year_in_review_builder.rb`, `reference/chatwoot/app/javascript/dashboard/api/yearInReview.js` | Done. API v2 year-in-review now matches the reused dashboard route and builder boundary: account-scoped `GET /api/v2/accounts/:account_id/year_in_review` is tracked and registered; response is raw JSON; missing `year` defaults to `2025`; the current user/account cache key is persisted in `ui_settings`; assigned yearly conversation totals, busiest day, and average first-response seconds are derived from persisted conversations/reporting events. | Review by `feat(reports): align year in review`; focused service tests cover aggregate payload, busiest day, average truncation, cache storage, cache reuse, and empty personality; handler tests cover raw response shape, auth, and invalid year; router/route-parity tests and route artifacts regenerate to `TOTAL: 951` and `401 exact, 13 parameter-compatible, 0 missing out of 414`; full `go test ./...` and `git diff --check` must pass. |
|
||||
@@ -256,6 +258,7 @@ This ledger records the committed parity checkpoints that future slices should b
|
||||
|
||||
| Commit | Scope | Verification summary | Follow-up state |
|
||||
| --- | --- | --- | --- |
|
||||
| `feat(channels): align whatsapp calls api` | Advances P3.37 account WhatsApp calls API parity by matching Chatwoot enterprise `Api::V1::Accounts::WhatsappCallsController`, `Whatsapp::CallService`, `Voice::CallMessageBuilder`, `Call`, WhatsApp call Jbuilder views/specs, route `237-245`, and reused dashboard `whatsappCallsAPI.js`. GoChat now exposes account-level show/initiate/accept/reject/terminate/upload-recording routes, persists WhatsApp calls through the `calls` model boundary, creates linked `voice_call` messages, updates call/message/conversation status for lifecycle actions, records permission-request attributes, and makes recording upload idempotent. | `go test ./internal/service ./internal/handler/api/v1 ./internal/router ./cmd/route_parity -run 'WhatsAppCall\|RegisterRoutes\|RouteParity' -count=1`; `go run ./cmd/dump_routes > docs/parity/gochat_routes.txt`; `go run ./cmd/route_parity`; full `go test ./...`; `git diff --check`. Route dump is `TOTAL: 958`; tracked route parity is `403 exact, 0 method-compatible, 18 parameter-compatible, 0 missing out of 421`. | P3.37 moves to Review for current WhatsApp calls evidence; continue Phase 2/3 drift audit, Phase 6 placeholder audit, B12 live smoke, or fresh reference/smoke drift. |
|
||||
| `feat(accounts): align onboarding update` | Advances P3.36 account onboarding route/payload parity by matching Chatwoot `Api::V1::Accounts::OnboardingsController#update`, account update/model Jbuilder payloads, route `58`, and reused dashboard onboarding API/store/Vue flow. GoChat now persists `accounts.custom_attributes`, exposes administrator-gated `PATCH /api/v1/accounts/:account_id/onboarding`, accepts the raw onboarding payload, merges permitted custom attributes, clears `onboarding_step` when finalizing account details, and returns the raw account serializer shape. | `go test ./internal/handler/api/v1 -run AccountHandler -count=1`; `go test ./internal/service -run AccountService -count=1`; `go test ./cmd/route_parity ./internal/router -run 'RouteParity\|RegisterRoutes' -count=1`; `go run ./cmd/dump_routes > docs/parity/gochat_routes.txt`; `go run ./cmd/route_parity`; full `go test ./...`; `git diff --check`. Route dump is `TOTAL: 952`; tracked route parity is `402 exact, 0 method-compatible, 13 parameter-compatible, 0 missing out of 415`. | P3.36 moves to Review for current onboarding/account serializer evidence; continue Phase 2/3 drift audit, Phase 6 placeholder audit, B12 live smoke, or fresh reference/smoke drift. |
|
||||
| `feat(notifications): tighten chatwoot scoping` | Advances P3.35 account notification scoping hardening by matching Chatwoot `NotificationsController#fetch_notification`, `NotificationFinder`, notification routes `283-291`, index Jbuilder behavior, and reused dashboard `notifications.js`. GoChat now scopes show, update/read, destroy, snooze, unread, and unread_count to the current user and account; parses snooze timestamps as Unix seconds; merges `meta.last_snoozed_at: null`; treats missing `snoozed_until` as a scoped no-op; and keeps destroy as empty `200 OK`. | `go test ./internal/handler/api/v1 -run Notification -count=1`; `go test ./internal/repository -run Notification -count=1`; `go test ./internal/service -run Notification -count=1`; full `go test ./...`; `git diff --check`. Route artifacts are unchanged. | P3.35 moves to Review for current notification action scoping evidence; continue Phase 2/3 drift audit, Phase 6 placeholder audit, B12 live smoke, or fresh reference/smoke drift. |
|
||||
| `feat(reports): align year in review` | Advances P3.34 API v2 year-in-review parity by matching Chatwoot `Api::V2::Accounts::YearInReviewsController#show`, `YearInReviewBuilder`, route `505`, current-user `ui_settings` caching, and reused dashboard `api/yearInReview.js`. GoChat now exposes `GET /api/v2/accounts/:account_id/year_in_review`, returns raw `{ year, total_conversations, busiest_day, support_personality }`, defaults missing `year` to `2025`, stores cached data under `year_in_review_<account_id>_<year>`, counts current-user assigned conversations in the selected year, formats the busiest day as `Mon DD`, and truncates average `first_response` seconds. | `go test ./internal/service -run YearInReview -count=1`; `go test ./internal/handler/api/v1 -run YearInReview -count=1`; `go test ./internal/router ./cmd/route_parity -run 'RegisterRoutes\|RouteParity' -count=1`; `go run ./cmd/dump_routes > docs/parity/gochat_routes.txt`; `go run ./cmd/route_parity`; full `go test ./...`; `git diff --check`. Route dump is `TOTAL: 951`; tracked route parity is `401 exact, 0 method-compatible, 13 parameter-compatible, 0 missing out of 414`. | P3.34 moves to Review for current API v2 year-in-review evidence; continue Phase 2/3 drift audit, Phase 6 placeholder audit, B12 live smoke, or fresh reference/smoke drift. |
|
||||
@@ -2273,6 +2276,7 @@ Verification milestone gates:
|
||||
|
||||
## Progress Log
|
||||
|
||||
- 2026-06-06: P3.37 account WhatsApp calls checkpoint prepared as `feat(channels): align whatsapp calls api`; audited Chatwoot enterprise WhatsApp call routes `237-245`, `WhatsappCallsController`, WhatsApp call Jbuilder views, `Whatsapp::CallService`, `Voice::CallMessageBuilder`, `Call`, controller specs, and reused dashboard `whatsappCallsAPI.js`. GoChat now registers account-level WhatsApp call show/initiate/accept/reject/terminate/upload-recording routes, persists calls through the `calls` model, creates linked `voice_call` messages for outbound calls, updates message/conversation call status through lifecycle actions, records permission-request conversation attributes, and returns idempotent recording upload status. Focused WhatsApp call service/handler/router/route-parity tests, route dump/parity regeneration, full `go test ./...`, and `git diff --check` passed; route dump is `TOTAL: 958` and tracked route parity is `403 exact, 0 method-compatible, 18 parameter-compatible, 0 missing out of 421`.
|
||||
- 2026-06-06: P3.36 account onboarding checkpoint prepared as `feat(accounts): align onboarding update`; audited Chatwoot account onboarding route `58`, `Api::V1::Accounts::OnboardingsController#update`, account update/model Jbuilder views, and reused dashboard `api/onboarding.js`, account store `finishOnboarding`, and onboarding Vue payload. GoChat now registers administrator-gated `PATCH /api/v1/accounts/:account_id/onboarding`, adds persisted `accounts.custom_attributes`, accepts the raw frontend payload, merges permitted onboarding custom attributes, clears `onboarding_step` when finalizing account details, and returns the raw account serializer with Chatwoot selected custom-attribute fields. Focused account handler/service tests, router/route-parity tests, route dump/parity regeneration, full `go test ./...`, and `git diff --check` passed; route dump is `TOTAL: 952` and tracked route parity is `402 exact, 0 method-compatible, 13 parameter-compatible, 0 missing out of 415`.
|
||||
- 2026-06-06: P3.35 account notification scoping checkpoint prepared as `feat(notifications): tighten chatwoot scoping`; audited Chatwoot notification routes `283-291`, `NotificationsController`, `NotificationFinder`, notification index Jbuilder, and reused dashboard `api/notifications.js`. Account notification show/update/destroy/snooze/unread/unread_count now use current-user/current-account scope, cross-account and cross-user rows return `404`, `snoozed_until` is parsed as Unix seconds, snooze merges `meta.last_snoozed_at: null`, absent `snoozed_until` is a scoped no-op returning the notification, and destroy returns empty `200 OK`. Focused notification handler/repository/service tests, full `go test ./...`, and `git diff --check` passed. No route artifacts changed; continue Phase 2/3 drift audit, Phase 6 placeholder audit, or B12 live smoke.
|
||||
- 2026-06-06: P3.13 dashboard apps checkpoint prepared as `feat(dashboard-apps): align chatwoot payloads`; audited Chatwoot `routes.rb:130`, `DashboardAppsController`, dashboard app Jbuilder views, `DashboardApp` content schema, reused dashboard `api/dashboardApps.js`, Vuex `dashboardApps` store, and DashboardApps modal/index components. Dashboard app routes are now tracked, create/update accept raw frontend bodies as well as `{ dashboard_app: ... }`, create returns raw `200 OK` payloads, list/show/create/update return only `id`, `title`, `content`, and `created_at`, show/update/delete are account-scoped, delete returns `204`, and content validation rejects empty arrays, non-frame widgets, and non-HTTP(S) URLs. Focused dashboard app handler/service tests, combined handler/service/repository/router/route-parity tests, route dump/parity regeneration, full `go test ./...`, and `git diff --check` passed; route dump remains `TOTAL: 907` and tracked route parity is `357 exact, 0 method-compatible, 13 parameter-compatible, 0 missing out of 370`.
|
||||
|
||||
@@ -388,6 +388,7 @@ GET /api/v1/accounts/:account_id/users
|
||||
GET /api/v1/accounts/:account_id/web_widgets/offline_messages
|
||||
GET /api/v1/accounts/:account_id/webhooks
|
||||
GET /api/v1/accounts/:account_id/webhooks/:webhook_id
|
||||
GET /api/v1/accounts/:account_id/whatsapp_calls/:call_id
|
||||
GET /api/v1/accounts/all
|
||||
GET /api/v1/auth/confirm_email
|
||||
GET /api/v1/auth/mfa/status
|
||||
@@ -773,6 +774,11 @@ POST /api/v1/accounts/:account_id/upload
|
||||
POST /api/v1/accounts/:account_id/users
|
||||
POST /api/v1/accounts/:account_id/webhooks
|
||||
POST /api/v1/accounts/:account_id/whatsapp/authorization
|
||||
POST /api/v1/accounts/:account_id/whatsapp_calls/:call_id/accept
|
||||
POST /api/v1/accounts/:account_id/whatsapp_calls/:call_id/reject
|
||||
POST /api/v1/accounts/:account_id/whatsapp_calls/:call_id/terminate
|
||||
POST /api/v1/accounts/:account_id/whatsapp_calls/:call_id/upload_recording
|
||||
POST /api/v1/accounts/:account_id/whatsapp_calls/initiate
|
||||
POST /api/v1/auth/login
|
||||
POST /api/v1/auth/login/mfa
|
||||
POST /api/v1/auth/mfa/backup_codes
|
||||
@@ -950,4 +956,4 @@ PUT /public/api/v1/csat_survey/:id
|
||||
PUT /public/api/v1/inboxes/:inbox_id/contacts/:contact_id
|
||||
PUT /public/api/v1/inboxes/:inbox_id/contacts/:contact_id/conversations/:conversation_id/messages/:message_id
|
||||
PUT /widget/direct_uploads/:upload_uuid
|
||||
TOTAL: 952
|
||||
TOTAL: 958
|
||||
|
||||
@@ -7,7 +7,7 @@ Generated from:
|
||||
|
||||
This report covers tracked frontend-critical Chatwoot routes from `reference/chatwoot/config/routes.rb`, including API v1 account routes, Captain/Copilot, assignment policies, widget/public APIs, and API v2 reports. Ruby is not installed in the workspace, so Chatwoot routes are sourced from static route declarations instead of `bin/rails routes`.
|
||||
|
||||
Summary: 402 exact, 0 method-compatible, 13 parameter-compatible, 0 missing out of 415 tracked critical routes.
|
||||
Summary: 403 exact, 0 method-compatible, 18 parameter-compatible, 0 missing out of 421 tracked critical routes.
|
||||
|
||||
## Missing Critical Routes
|
||||
|
||||
@@ -34,12 +34,17 @@ These routes exist with equivalent method and path shape but different parameter
|
||||
| DELETE | `/api/v1/accounts/:account_id/dashboard_apps/:id` | `/api/v1/accounts/:account_id/dashboard_apps/:dashboard_app_id` | `api/v1/accounts/dashboard_apps#destroy` | `routes.rb:130` | parameter-compatible |
|
||||
| GET | `/api/v1/accounts/:account_id/agent_capacity_policies/:agent_capacity_policy_id/users` | `/api/v1/accounts/:account_id/agent_capacity_policies/:id/users` | `api/v1/accounts/agent_capacity_policies/users#index` | `routes.rb:125` | parameter-compatible |
|
||||
| GET | `/api/v1/accounts/:account_id/dashboard_apps/:id` | `/api/v1/accounts/:account_id/dashboard_apps/:dashboard_app_id` | `api/v1/accounts/dashboard_apps#show` | `routes.rb:130` | parameter-compatible |
|
||||
| GET | `/api/v1/accounts/:account_id/whatsapp_calls/:id` | `/api/v1/accounts/:account_id/whatsapp_calls/:call_id` | `api/v1/accounts/whatsapp_calls#show` | `routes.rb:237` | parameter-compatible |
|
||||
| GET | `/hc/:slug/articles/:article_slug.md` | `/hc/:slug/articles/:article_slug` | `public/api/v1/portals/articles#show_markdown` | `routes.rb:599` | parameter-compatible |
|
||||
| GET | `/hc/:slug/articles/:article_slug.png` | `/hc/:slug/articles/:article_slug` | `public/api/v1/portals/articles#tracking_pixel` | `routes.rb:598` | parameter-compatible |
|
||||
| PATCH | `/api/v1/accounts/:account_id/agent_capacity_policies/:agent_capacity_policy_id/inbox_limits/:id` | `/api/v1/accounts/:account_id/agent_capacity_policies/:id/inbox_limits/:limit_id` | `api/v1/accounts/agent_capacity_policies/inbox_limits#update` | `routes.rb:126` | parameter-compatible |
|
||||
| PATCH | `/api/v1/accounts/:account_id/dashboard_apps/:id` | `/api/v1/accounts/:account_id/dashboard_apps/:dashboard_app_id` | `api/v1/accounts/dashboard_apps#update` | `routes.rb:130` | parameter-compatible |
|
||||
| POST | `/api/v1/accounts/:account_id/agent_capacity_policies/:agent_capacity_policy_id/inbox_limits` | `/api/v1/accounts/:account_id/agent_capacity_policies/:id/inbox_limits` | `api/v1/accounts/agent_capacity_policies/inbox_limits#create` | `routes.rb:126` | parameter-compatible |
|
||||
| POST | `/api/v1/accounts/:account_id/agent_capacity_policies/:agent_capacity_policy_id/users` | `/api/v1/accounts/:account_id/agent_capacity_policies/:id/users` | `api/v1/accounts/agent_capacity_policies/users#create` | `routes.rb:125` | parameter-compatible |
|
||||
| POST | `/api/v1/accounts/:account_id/whatsapp_calls/:id/accept` | `/api/v1/accounts/:account_id/whatsapp_calls/:call_id/accept` | `api/v1/accounts/whatsapp_calls#accept` | `routes.rb:239` | parameter-compatible |
|
||||
| POST | `/api/v1/accounts/:account_id/whatsapp_calls/:id/reject` | `/api/v1/accounts/:account_id/whatsapp_calls/:call_id/reject` | `api/v1/accounts/whatsapp_calls#reject` | `routes.rb:240` | parameter-compatible |
|
||||
| POST | `/api/v1/accounts/:account_id/whatsapp_calls/:id/terminate` | `/api/v1/accounts/:account_id/whatsapp_calls/:call_id/terminate` | `api/v1/accounts/whatsapp_calls#terminate` | `routes.rb:241` | parameter-compatible |
|
||||
| POST | `/api/v1/accounts/:account_id/whatsapp_calls/:id/upload_recording` | `/api/v1/accounts/:account_id/whatsapp_calls/:call_id/upload_recording` | `api/v1/accounts/whatsapp_calls#upload_recording` | `routes.rb:242` | parameter-compatible |
|
||||
| PUT | `/api/v1/accounts/:account_id/agent_capacity_policies/:agent_capacity_policy_id/inbox_limits/:id` | `/api/v1/accounts/:account_id/agent_capacity_policies/:id/inbox_limits/:limit_id` | `api/v1/accounts/agent_capacity_policies/inbox_limits#update` | `routes.rb:126` | parameter-compatible |
|
||||
| PUT | `/api/v1/accounts/:account_id/dashboard_apps/:id` | `/api/v1/accounts/:account_id/dashboard_apps/:dashboard_app_id` | `api/v1/accounts/dashboard_apps#update` | `routes.rb:130` | parameter-compatible |
|
||||
|
||||
@@ -384,6 +389,7 @@ These routes exist with equivalent method and path shape but different parameter
|
||||
| POST | `/api/v1/accounts/:account_id/update_active_at` | `/api/v1/accounts/:account_id/update_active_at` | `api/v1/accounts#update_active_at` | `routes.rb:49` | exact |
|
||||
| POST | `/api/v1/accounts/:account_id/webhooks` | `/api/v1/accounts/:account_id/webhooks` | `api/v1/accounts/webhooks#create` | `routes.rb:342` | exact |
|
||||
| POST | `/api/v1/accounts/:account_id/whatsapp/authorization` | `/api/v1/accounts/:account_id/whatsapp/authorization` | `api/v1/accounts/whatsapp/authorizations#create` | `routes.rb:339` | exact |
|
||||
| POST | `/api/v1/accounts/:account_id/whatsapp_calls/initiate` | `/api/v1/accounts/:account_id/whatsapp_calls/initiate` | `api/v1/accounts/whatsapp_calls#initiate` | `routes.rb:245` | exact |
|
||||
| POST | `/api/v1/notification_subscriptions` | `/api/v1/notification_subscriptions` | `api/v1/notification_subscriptions#create` | `routes.rb:440` | exact |
|
||||
| POST | `/api/v1/widget/config` | `/api/v1/widget/config` | `api/v1/widget/config#create` | `routes.rb:444` | exact |
|
||||
| POST | `/api/v1/widget/contact/destroy_custom_attributes` | `/api/v1/widget/contact/destroy_custom_attributes` | `api/v1/widget/contact#destroy_custom_attributes` | `routes.rb:460` | exact |
|
||||
|
||||
Reference in New Issue
Block a user