feat(routes): expand account parity coverage

This commit is contained in:
2026-06-04 20:44:20 +08:00
parent 14fa51d0b6
commit 14e784c51d
7 changed files with 253 additions and 20 deletions
+65 -1
View File
@@ -29,6 +29,19 @@ var criticalRoutes = []route{
{Method: "POST", Path: "/api/v1/accounts/:account_id/update_active_at", Controller: "api/v1/accounts#update_active_at", Source: "routes.rb:49"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/cache_keys", Controller: "api/v1/accounts#cache_keys", Source: "routes.rb:50"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/agents", Controller: "api/v1/accounts/agents#index", Source: "routes.rb:59"},
{Method: "POST", Path: "/api/v1/accounts/:account_id/agents", Controller: "api/v1/accounts/agents#create", Source: "routes.rb:59"},
{Method: "PUT", Path: "/api/v1/accounts/:account_id/agents/:agent_id", Controller: "api/v1/accounts/agents#update", Source: "routes.rb:59"},
{Method: "DELETE", Path: "/api/v1/accounts/:account_id/agents/:agent_id", Controller: "api/v1/accounts/agents#destroy", Source: "routes.rb:59"},
{Method: "POST", Path: "/api/v1/accounts/:account_id/agents/bulk_create", Controller: "api/v1/accounts/agents#bulk_create", Source: "routes.rb:60"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/assignable_agents", Controller: "api/v1/accounts/assignable_agents#index", Source: "routes.rb:104"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/audit_logs/", Controller: "api/v1/accounts/audit_logs#show", Source: "routes.rb:105"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/canned_responses/", Controller: "api/v1/accounts/canned_responses#index", Source: "routes.rb:114"},
{Method: "POST", Path: "/api/v1/accounts/:account_id/canned_responses/", Controller: "api/v1/accounts/canned_responses#create", Source: "routes.rb:114"},
{Method: "PUT", Path: "/api/v1/accounts/:account_id/canned_responses/:id", Controller: "api/v1/accounts/canned_responses#update", Source: "routes.rb:114"},
{Method: "DELETE", Path: "/api/v1/accounts/:account_id/canned_responses/:id", Controller: "api/v1/accounts/canned_responses#destroy", Source: "routes.rb:114"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/conversations/", Controller: "api/v1/accounts/conversations#index", Source: "routes.rb:134"},
{Method: "POST", Path: "/api/v1/accounts/:account_id/conversations/", Controller: "api/v1/accounts/conversations#create", Source: "routes.rb:134"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/conversations/:conversation_id", Controller: "api/v1/accounts/conversations#show", Source: "routes.rb:134"},
@@ -74,6 +87,8 @@ var criticalRoutes = []route{
{Method: "GET", Path: "/api/v1/accounts/:account_id/companies/:company_id/contacts", Controller: "api/v1/accounts/companies/contacts#index", Source: "routes.rb:188"},
{Method: "POST", Path: "/api/v1/accounts/:account_id/companies/:company_id/contacts/:contact_id", Controller: "api/v1/accounts/companies/contacts#create", Source: "routes.rb:188"},
{Method: "DELETE", Path: "/api/v1/accounts/:account_id/companies/:company_id/contacts/:contact_id", Controller: "api/v1/accounts/companies/contacts#destroy", Source: "routes.rb:188"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/companies/:company_id/conversations", Controller: "api/v1/accounts/companies/conversations#index", Source: "routes.rb:193"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/companies/:company_id/notes", Controller: "api/v1/accounts/companies/notes#index", Source: "routes.rb:194"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/search", Controller: "api/v1/accounts/search#index", Source: "routes.rb:170"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/search/conversations", Controller: "api/v1/accounts/search#conversations", Source: "routes.rb:173"},
@@ -88,8 +103,57 @@ var criticalRoutes = []route{
{Method: "DELETE", Path: "/api/v1/accounts/:account_id/inboxes/:inbox_id", Controller: "api/v1/accounts/inboxes#destroy", Source: "routes.rb:252"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/inboxes/:inbox_id/assignable_agents", Controller: "api/v1/accounts/inboxes#assignable_agents", Source: "routes.rb:253"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/inboxes/:inbox_id/campaigns", Controller: "api/v1/accounts/inboxes#campaigns", Source: "routes.rb:254"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/inboxes/:inbox_id/agent_bot", Controller: "api/v1/accounts/inboxes#agent_bot", Source: "routes.rb:255"},
{Method: "POST", Path: "/api/v1/accounts/:account_id/inboxes/:inbox_id/set_agent_bot", Controller: "api/v1/accounts/inboxes#set_agent_bot", Source: "routes.rb:256"},
{Method: "DELETE", Path: "/api/v1/accounts/:account_id/inboxes/:inbox_id/avatar", Controller: "api/v1/accounts/inboxes#avatar", Source: "routes.rb:257"},
{Method: "POST", Path: "/api/v1/accounts/:account_id/inboxes/:inbox_id/sync_templates", Controller: "api/v1/accounts/inboxes#sync_templates", Source: "routes.rb:258"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/inboxes/:inbox_id/health", Controller: "api/v1/accounts/inboxes#health", Source: "routes.rb:259"},
{Method: "POST", Path: "/api/v1/accounts/:account_id/inboxes/:inbox_id/register_webhook", Controller: "api/v1/accounts/inboxes#register_webhook", Source: "routes.rb:260"},
{Method: "POST", Path: "/api/v1/accounts/:account_id/inboxes/:inbox_id/reset_secret", Controller: "api/v1/accounts/inboxes#reset_secret", Source: "routes.rb:262"},
{Method: "POST", Path: "/api/v1/accounts/:account_id/inbox_members/", Controller: "api/v1/accounts/inbox_members#create", Source: "routes.rb:275"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/inbox_members/:inbox_id", Controller: "api/v1/accounts/inbox_members#show", Source: "routes.rb:275"},
{Method: "PATCH", Path: "/api/v1/accounts/:account_id/inbox_members/", Controller: "api/v1/accounts/inbox_members#update", Source: "routes.rb:278"},
{Method: "DELETE", Path: "/api/v1/accounts/:account_id/inbox_members/", Controller: "api/v1/accounts/inbox_members#destroy", Source: "routes.rb:277"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/labels/", Controller: "api/v1/accounts/labels#index", Source: "routes.rb:281"},
{Method: "POST", Path: "/api/v1/accounts/:account_id/labels/", Controller: "api/v1/accounts/labels#create", Source: "routes.rb:281"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/labels/:tag_id", Controller: "api/v1/accounts/labels#show", Source: "routes.rb:281"},
{Method: "PUT", Path: "/api/v1/accounts/:account_id/labels/:tag_id", Controller: "api/v1/accounts/labels#update", Source: "routes.rb:281"},
{Method: "DELETE", Path: "/api/v1/accounts/:account_id/labels/:tag_id", Controller: "api/v1/accounts/labels#destroy", Source: "routes.rb:281"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/notifications/", Controller: "api/v1/accounts/notifications#index", Source: "routes.rb:283"},
{Method: "PUT", Path: "/api/v1/accounts/:account_id/notifications/:notification_id", Controller: "api/v1/accounts/notifications#update", Source: "routes.rb:283"},
{Method: "DELETE", Path: "/api/v1/accounts/:account_id/notifications/:notification_id", Controller: "api/v1/accounts/notifications#destroy", Source: "routes.rb:283"},
{Method: "POST", Path: "/api/v1/accounts/:account_id/notifications/read_all", Controller: "api/v1/accounts/notifications#read_all", Source: "routes.rb:285"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/notifications/unread_count", Controller: "api/v1/accounts/notifications#unread_count", Source: "routes.rb:286"},
{Method: "POST", Path: "/api/v1/accounts/:account_id/notifications/destroy_all", Controller: "api/v1/accounts/notifications#destroy_all", Source: "routes.rb:287"},
{Method: "POST", Path: "/api/v1/accounts/:account_id/notifications/:notification_id/snooze", Controller: "api/v1/accounts/notifications#snooze", Source: "routes.rb:290"},
{Method: "POST", Path: "/api/v1/accounts/:account_id/notifications/:notification_id/unread", Controller: "api/v1/accounts/notifications#unread", Source: "routes.rb:291"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/notification_settings/", Controller: "api/v1/accounts/notification_settings#show", Source: "routes.rb:293"},
{Method: "PUT", Path: "/api/v1/accounts/:account_id/notification_settings/", Controller: "api/v1/accounts/notification_settings#update", Source: "routes.rb:293"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/teams/", Controller: "api/v1/accounts/teams#index", Source: "routes.rb:296"},
{Method: "POST", Path: "/api/v1/accounts/:account_id/teams/", Controller: "api/v1/accounts/teams#create", Source: "routes.rb:296"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/teams/:team_id", Controller: "api/v1/accounts/teams#show", Source: "routes.rb:296"},
{Method: "PUT", Path: "/api/v1/accounts/:account_id/teams/:team_id", Controller: "api/v1/accounts/teams#update", Source: "routes.rb:296"},
{Method: "DELETE", Path: "/api/v1/accounts/:account_id/teams/:team_id", Controller: "api/v1/accounts/teams#destroy", Source: "routes.rb:296"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/teams/:team_id/team_members/", Controller: "api/v1/accounts/teams/team_members#index", Source: "routes.rb:297"},
{Method: "POST", Path: "/api/v1/accounts/:account_id/teams/:team_id/team_members/", Controller: "api/v1/accounts/teams/team_members#create", Source: "routes.rb:297"},
{Method: "PATCH", Path: "/api/v1/accounts/:account_id/teams/:team_id/team_members/", Controller: "api/v1/accounts/teams/team_members#update", Source: "routes.rb:300"},
{Method: "DELETE", Path: "/api/v1/accounts/:account_id/teams/:team_id/team_members/", Controller: "api/v1/accounts/teams/team_members#destroy", Source: "routes.rb:299"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/custom_attribute_definitions/", Controller: "api/v1/accounts/custom_attribute_definitions#index", Source: "routes.rb:250"},
{Method: "POST", Path: "/api/v1/accounts/:account_id/custom_attribute_definitions/", Controller: "api/v1/accounts/custom_attribute_definitions#create", Source: "routes.rb:250"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/custom_attribute_definitions/:id", Controller: "api/v1/accounts/custom_attribute_definitions#show", Source: "routes.rb:250"},
{Method: "PUT", Path: "/api/v1/accounts/:account_id/custom_attribute_definitions/:id", Controller: "api/v1/accounts/custom_attribute_definitions#update", Source: "routes.rb:250"},
{Method: "DELETE", Path: "/api/v1/accounts/:account_id/custom_attribute_definitions/:id", Controller: "api/v1/accounts/custom_attribute_definitions#destroy", Source: "routes.rb:250"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/custom_filters/", Controller: "api/v1/accounts/custom_filters#index", Source: "routes.rb:251"},
{Method: "POST", Path: "/api/v1/accounts/:account_id/custom_filters/", Controller: "api/v1/accounts/custom_filters#create", Source: "routes.rb:251"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/custom_filters/:id", Controller: "api/v1/accounts/custom_filters#show", Source: "routes.rb:251"},
{Method: "PUT", Path: "/api/v1/accounts/:account_id/custom_filters/:id", Controller: "api/v1/accounts/custom_filters#update", Source: "routes.rb:251"},
{Method: "DELETE", Path: "/api/v1/accounts/:account_id/custom_filters/:id", Controller: "api/v1/accounts/custom_filters#destroy", Source: "routes.rb:251"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/automation_rules/", Controller: "api/v1/accounts/automation_rules#index", Source: "routes.rb:115"},
{Method: "POST", Path: "/api/v1/accounts/:account_id/automation_rules/", Controller: "api/v1/accounts/automation_rules#create", Source: "routes.rb:115"},
{Method: "GET", Path: "/api/v1/accounts/:account_id/automation_rules/:automation_id", Controller: "api/v1/accounts/automation_rules#show", Source: "routes.rb:115"},
@@ -220,7 +284,7 @@ func writeParity(path string, gochatPath string, chatwootPath string, gochatRout
b.WriteString("Generated from:\n\n")
b.WriteString("- GoChat route dump: `" + gochatPath + "`\n")
b.WriteString("- Chatwoot route source: `" + chatwootPath + "`\n\n")
b.WriteString("This first report covers frontend-critical API v1 account routes from `reference/chatwoot/config/routes.rb`. Ruby is not installed in the workspace, so Chatwoot routes are sourced from static route declarations instead of `bin/rails routes`.\n\n")
b.WriteString("This report covers tracked frontend-critical API v1 account routes from `reference/chatwoot/config/routes.rb`. Ruby is not installed in the workspace, so Chatwoot routes are sourced from static route declarations instead of `bin/rails routes`.\n\n")
b.WriteString(fmt.Sprintf("Summary: %d exact, %d method-compatible, %d parameter-compatible, %d missing out of %d tracked critical routes.\n\n", len(exact), len(methodCompatible), len(compatible), len(missing), len(criticalRoutes)))
b.WriteString("## Missing Critical Routes\n\n")
+16 -3
View File
@@ -17,9 +17,9 @@ Build GoChat as a Go backend that can directly reuse the frontend from `referenc
## Current Baseline
- `go test ./...` passes.
- Route dump succeeds with `TOTAL: 709`.
- Route dump succeeds with `TOTAL: 723`.
- Route parity artifacts now exist under `docs/parity/` and are generated by `cmd/route_parity`.
- First tracked frontend-critical API v1 route audit covers 81 Chatwoot routes: 81 exact, 0 method-compatible, 0 parameter-compatible, 0 missing.
- First tracked frontend-critical API v1 route audit covers 138 Chatwoot routes: 138 exact, 0 method-compatible, 0 parameter-compatible, 0 missing.
- 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.
@@ -153,10 +153,22 @@ Current Phase 2 route findings:
| Type | Count | Required action |
| --- | --- | --- |
| Exact tracked critical routes | 81 | Keep covered while expanding audit scope. |
| Exact tracked critical routes | 138 | Keep covered while expanding audit scope. |
| Method-compatible update routes | 0 | First tracked batch now has exact Rails-compatible method coverage. |
| Missing tracked critical routes | 0 | First tracked frontend-critical batch has no route-level gaps. |
Expanded tracked groups now covered by route parity:
| Area | Coverage |
| --- | --- |
| Agents and assignable agents | CRUD/bulk-create route coverage for `agents`, plus `assignable_agents#index`. |
| Canned responses | index/create/update/destroy account routes. |
| Custom attributes and custom filters | index/show/create/update/destroy account routes. |
| Labels and team membership | labels CRUD plus teams and team_members collection actions. |
| Notifications | account-scoped notifications, notification_settings, unread_count, read_all, destroy_all, snooze, unread. |
| Inbox members | Chatwoot account-scoped `inbox_members` create/show/update/destroy routes. |
| Inbox member actions | agent_bot, set_agent_bot, sync_templates, health, register_webhook, reset_secret, avatar. |
Closed tracked critical route gaps in this slice:
| Method | Path | Chatwoot controller | Implementation note |
@@ -363,3 +375,4 @@ env GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go run ./cmd/d
- 2026-06-04: Phase 1 indexing hooks wired for conversations, messages, contacts, companies, and articles. Create/update/delete paths now call the service-layer `SearchIndexer` boundary, bootstrap injects the Meilisearch-backed search service, and unit tests cover each entity hook path.
- 2026-06-04: Phase 2 route parity tracking added. Ruby/Bundler are unavailable in this workspace, so `cmd/route_parity` records static Chatwoot route DSL declarations from `reference/chatwoot/config/routes.rb`, consumes `cmd/dump_routes` output, and writes `docs/parity/route_parity.md`. First tracked critical route summary: 76 exact, 2 method-compatible, 0 parameter-compatible, 3 missing out of 81; GoChat route dump remains `TOTAL: 704`.
- 2026-06-04: First tracked Phase 2 route gaps closed. Added Chatwoot-compatible contact conversations, contact export POST, conversation `toggle_priority`, and PUT aliases for conversation/message updates; fixed contact handlers to accept `:account_id` as well as legacy `:id`. Regenerated parity report: 81 exact, 0 method-compatible, 0 parameter-compatible, 0 missing out of 81; route dump now reports `TOTAL: 709`.
- 2026-06-04: Expanded Phase 2 route parity from 81 to 138 tracked frontend-critical account routes. Added account-scoped notification routes, `notification_settings` PUT alias, and Chatwoot account-level `inbox_members` create/show/update/destroy handlers. Regenerated parity report: 138 exact, 0 method-compatible, 0 parameter-compatible, 0 missing out of 138; route dump now reports `TOTAL: 723`.
+16 -2
View File
@@ -48,7 +48,7 @@ DELETE /api/v1/accounts/:account_id/dashboard_apps/:dashboard_app_id
DELETE /api/v1/accounts/:account_id/dashboard_apps/:dashboard_app_id/widgets/:widget_index
DELETE /api/v1/accounts/:account_id/google_channels/:google_id
DELETE /api/v1/accounts/:account_id/hooks/:id
DELETE /api/v1/accounts/:account_id/inbox_members/:user_id
DELETE /api/v1/accounts/:account_id/inbox_members/
DELETE /api/v1/accounts/:account_id/inboxes/:inbox_id
DELETE /api/v1/accounts/:account_id/inboxes/:inbox_id/assignment_policy/:policy_id
DELETE /api/v1/accounts/:account_id/inboxes/:inbox_id/assignment_policy_v2/
@@ -73,6 +73,8 @@ DELETE /api/v1/accounts/:account_id/labels/:tag_id
DELETE /api/v1/accounts/:account_id/macros/:macro_id
DELETE /api/v1/accounts/:account_id/microsoft_channels/:ms_id
DELETE /api/v1/accounts/:account_id/notification_subscriptions/:identifier
DELETE /api/v1/accounts/:account_id/notifications/:notification_id
DELETE /api/v1/accounts/:account_id/notifications/destroy_all
DELETE /api/v1/accounts/:account_id/platform_apps/:platform_app_id
DELETE /api/v1/accounts/:account_id/platform_apps/:platform_app_id/permissibles/:permissible_id
DELETE /api/v1/accounts/:account_id/portals/:portal_id
@@ -222,6 +224,7 @@ GET /api/v1/accounts/:account_id/google/webhooks
GET /api/v1/accounts/:account_id/google_channels/authorization
GET /api/v1/accounts/:account_id/hooks/
GET /api/v1/accounts/:account_id/hooks/:id
GET /api/v1/accounts/:account_id/inbox_members/:inbox_id
GET /api/v1/accounts/:account_id/inboxes/
GET /api/v1/accounts/:account_id/inboxes/:inbox_id
GET /api/v1/accounts/:account_id/inboxes/:inbox_id/agent_bot
@@ -272,6 +275,9 @@ GET /api/v1/accounts/:account_id/microsoft/oauth
GET /api/v1/accounts/:account_id/microsoft/webhooks
GET /api/v1/accounts/:account_id/microsoft_channels/authorization
GET /api/v1/accounts/:account_id/notification_settings/
GET /api/v1/accounts/:account_id/notifications/
GET /api/v1/accounts/:account_id/notifications/:notification_id
GET /api/v1/accounts/:account_id/notifications/unread_count
GET /api/v1/accounts/:account_id/platform_apps/
GET /api/v1/accounts/:account_id/platform_apps/:platform_app_id
GET /api/v1/accounts/:account_id/platform_apps/:platform_app_id/access_tokens
@@ -403,6 +409,7 @@ PATCH /api/v1/accounts/:account_id/csat_survey_responses/:id
PATCH /api/v1/accounts/:account_id/custom_attribute_definitions/:id
PATCH /api/v1/accounts/:account_id/custom_filters/:id
PATCH /api/v1/accounts/:account_id/dashboard_apps/:dashboard_app_id
PATCH /api/v1/accounts/:account_id/inbox_members/
PATCH /api/v1/accounts/:account_id/inboxes/:inbox_id/email_channels/:em_id
PATCH /api/v1/accounts/:account_id/inboxes/:inbox_id/inbox_limits/:id
PATCH /api/v1/accounts/:account_id/inboxes/:inbox_id/instagram_channels/:ig_id
@@ -413,6 +420,7 @@ PATCH /api/v1/accounts/:account_id/inboxes/:inbox_id/tiktok_channels/:tt_id
PATCH /api/v1/accounts/:account_id/inboxes/:inbox_id/twilio_sms_channels/:tw_id
PATCH /api/v1/accounts/:account_id/integrations/slack/
PATCH /api/v1/accounts/:account_id/notification_settings/
PATCH /api/v1/accounts/:account_id/notifications/:notification_id
PATCH /api/v1/accounts/:account_id/teams/:team_id/team_members/
PATCH /platform/api/v1/accounts/:account_id
PATCH /platform/api/v1/users/:id
@@ -560,6 +568,10 @@ POST /api/v1/accounts/:account_id/macros/:macro_id/toggle_active
POST /api/v1/accounts/:account_id/microsoft/webhooks
POST /api/v1/accounts/:account_id/microsoft_channels/oauth_callback
POST /api/v1/accounts/:account_id/notification_subscriptions/
POST /api/v1/accounts/:account_id/notifications/:notification_id/snooze
POST /api/v1/accounts/:account_id/notifications/:notification_id/unread
POST /api/v1/accounts/:account_id/notifications/destroy_all
POST /api/v1/accounts/:account_id/notifications/read_all
POST /api/v1/accounts/:account_id/platform_apps/
POST /api/v1/accounts/:account_id/platform_apps/:platform_app_id/permissibles
POST /api/v1/accounts/:account_id/platform_apps/:platform_app_id/regenerate_access_token
@@ -680,6 +692,8 @@ PUT /api/v1/accounts/:account_id/inboxes/:inbox_id/working_hours
PUT /api/v1/accounts/:account_id/integrations/hooks/:id
PUT /api/v1/accounts/:account_id/labels/:tag_id
PUT /api/v1/accounts/:account_id/macros/:macro_id
PUT /api/v1/accounts/:account_id/notification_settings/
PUT /api/v1/accounts/:account_id/notifications/:notification_id
PUT /api/v1/accounts/:account_id/platform_apps/:platform_app_id
PUT /api/v1/accounts/:account_id/portals/:portal_id
PUT /api/v1/accounts/:account_id/portals/:portal_id/articles/:article_id
@@ -707,4 +721,4 @@ PUT /platform/api/v1/banners/:id
PUT /platform/api/v1/installation_configs/:id
PUT /webhooks/:channel_type/:identifier
TRACE /webhooks/:channel_type/:identifier
TOTAL: 709
TOTAL: 723
+59 -2
View File
@@ -5,9 +5,9 @@ Generated from:
- GoChat route dump: `docs/parity/gochat_routes.txt`
- Chatwoot route source: `reference/chatwoot/config/routes.rb`
This first report covers frontend-critical API v1 account routes from `reference/chatwoot/config/routes.rb`. Ruby is not installed in the workspace, so Chatwoot routes are sourced from static route declarations instead of `bin/rails routes`.
This report covers tracked frontend-critical API v1 account routes from `reference/chatwoot/config/routes.rb`. Ruby is not installed in the workspace, so Chatwoot routes are sourced from static route declarations instead of `bin/rails routes`.
Summary: 81 exact, 0 method-compatible, 0 parameter-compatible, 0 missing out of 81 tracked critical routes.
Summary: 138 exact, 0 method-compatible, 0 parameter-compatible, 0 missing out of 138 tracked critical routes.
## Missing Critical Routes
@@ -35,23 +35,39 @@ These routes exist with equivalent method and path shape but different parameter
| Method | Chatwoot Path | GoChat Match | Controller | Source | Status |
| --- | --- | --- | --- | --- | --- |
| DELETE | `/api/v1/accounts/:account_id/agents/:agent_id` | `/api/v1/accounts/:account_id/agents/:agent_id` | `api/v1/accounts/agents#destroy` | `routes.rb:59` | exact |
| DELETE | `/api/v1/accounts/:account_id/automation_rules/:automation_id` | `/api/v1/accounts/:account_id/automation_rules/:automation_id` | `api/v1/accounts/automation_rules#destroy` | `routes.rb:115` | exact |
| DELETE | `/api/v1/accounts/:account_id/canned_responses/:id` | `/api/v1/accounts/:account_id/canned_responses/:id` | `api/v1/accounts/canned_responses#destroy` | `routes.rb:114` | exact |
| DELETE | `/api/v1/accounts/:account_id/companies/:company_id` | `/api/v1/accounts/:account_id/companies/:company_id` | `api/v1/accounts/companies#destroy` | `routes.rb:179` | exact |
| DELETE | `/api/v1/accounts/:account_id/companies/:company_id/contacts/:contact_id` | `/api/v1/accounts/:account_id/companies/:company_id/contacts/:contact_id` | `api/v1/accounts/companies/contacts#destroy` | `routes.rb:188` | exact |
| DELETE | `/api/v1/accounts/:account_id/contacts/:contact_id` | `/api/v1/accounts/:account_id/contacts/:contact_id` | `api/v1/accounts/contacts#destroy` | `routes.rb:197` | exact |
| DELETE | `/api/v1/accounts/:account_id/conversations/:conversation_id` | `/api/v1/accounts/:account_id/conversations/:conversation_id` | `api/v1/accounts/conversations#destroy` | `routes.rb:134` | exact |
| DELETE | `/api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id` | `/api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id` | `api/v1/accounts/conversations/messages#destroy` | `routes.rb:142` | exact |
| DELETE | `/api/v1/accounts/:account_id/custom_attribute_definitions/:id` | `/api/v1/accounts/:account_id/custom_attribute_definitions/:id` | `api/v1/accounts/custom_attribute_definitions#destroy` | `routes.rb:250` | exact |
| DELETE | `/api/v1/accounts/:account_id/custom_filters/:id` | `/api/v1/accounts/:account_id/custom_filters/:id` | `api/v1/accounts/custom_filters#destroy` | `routes.rb:251` | exact |
| DELETE | `/api/v1/accounts/:account_id/inbox_members/` | `/api/v1/accounts/:account_id/inbox_members/` | `api/v1/accounts/inbox_members#destroy` | `routes.rb:277` | exact |
| DELETE | `/api/v1/accounts/:account_id/inboxes/:inbox_id` | `/api/v1/accounts/:account_id/inboxes/:inbox_id` | `api/v1/accounts/inboxes#destroy` | `routes.rb:252` | exact |
| DELETE | `/api/v1/accounts/:account_id/inboxes/:inbox_id/avatar` | `/api/v1/accounts/:account_id/inboxes/:inbox_id/avatar` | `api/v1/accounts/inboxes#avatar` | `routes.rb:257` | exact |
| DELETE | `/api/v1/accounts/:account_id/labels/:tag_id` | `/api/v1/accounts/:account_id/labels/:tag_id` | `api/v1/accounts/labels#destroy` | `routes.rb:281` | exact |
| DELETE | `/api/v1/accounts/:account_id/macros/:macro_id` | `/api/v1/accounts/:account_id/macros/:macro_id` | `api/v1/accounts/macros#destroy` | `routes.rb:118` | exact |
| DELETE | `/api/v1/accounts/:account_id/notifications/:notification_id` | `/api/v1/accounts/:account_id/notifications/:notification_id` | `api/v1/accounts/notifications#destroy` | `routes.rb:283` | exact |
| DELETE | `/api/v1/accounts/:account_id/sla_policies/:id` | `/api/v1/accounts/:account_id/sla_policies/:id` | `api/v1/accounts/sla_policies#destroy` | `routes.rb:121` | exact |
| DELETE | `/api/v1/accounts/:account_id/teams/:team_id` | `/api/v1/accounts/:account_id/teams/:team_id` | `api/v1/accounts/teams#destroy` | `routes.rb:296` | exact |
| DELETE | `/api/v1/accounts/:account_id/teams/:team_id/team_members/` | `/api/v1/accounts/:account_id/teams/:team_id/team_members/` | `api/v1/accounts/teams/team_members#destroy` | `routes.rb:299` | exact |
| GET | `/api/v1/accounts/:account_id` | `/api/v1/accounts/:account_id` | `api/v1/accounts#show` | `routes.rb:47` | exact |
| GET | `/api/v1/accounts/:account_id/agent_capacity_policies/` | `/api/v1/accounts/:account_id/agent_capacity_policies/` | `api/v1/accounts/agent_capacity_policies#index` | `routes.rb:123` | exact |
| GET | `/api/v1/accounts/:account_id/agents` | `/api/v1/accounts/:account_id/agents` | `api/v1/accounts/agents#index` | `routes.rb:59` | exact |
| GET | `/api/v1/accounts/:account_id/assignable_agents` | `/api/v1/accounts/:account_id/assignable_agents` | `api/v1/accounts/assignable_agents#index` | `routes.rb:104` | exact |
| GET | `/api/v1/accounts/:account_id/audit_logs/` | `/api/v1/accounts/:account_id/audit_logs/` | `api/v1/accounts/audit_logs#show` | `routes.rb:105` | exact |
| GET | `/api/v1/accounts/:account_id/automation_rules/` | `/api/v1/accounts/:account_id/automation_rules/` | `api/v1/accounts/automation_rules#index` | `routes.rb:115` | exact |
| GET | `/api/v1/accounts/:account_id/automation_rules/:automation_id` | `/api/v1/accounts/:account_id/automation_rules/:automation_id` | `api/v1/accounts/automation_rules#show` | `routes.rb:115` | exact |
| GET | `/api/v1/accounts/:account_id/cache_keys` | `/api/v1/accounts/:account_id/cache_keys` | `api/v1/accounts#cache_keys` | `routes.rb:50` | exact |
| GET | `/api/v1/accounts/:account_id/canned_responses/` | `/api/v1/accounts/:account_id/canned_responses/` | `api/v1/accounts/canned_responses#index` | `routes.rb:114` | exact |
| GET | `/api/v1/accounts/:account_id/companies/` | `/api/v1/accounts/:account_id/companies/` | `api/v1/accounts/companies#index` | `routes.rb:179` | exact |
| GET | `/api/v1/accounts/:account_id/companies/:company_id` | `/api/v1/accounts/:account_id/companies/:company_id` | `api/v1/accounts/companies#show` | `routes.rb:179` | exact |
| GET | `/api/v1/accounts/:account_id/companies/:company_id/contacts` | `/api/v1/accounts/:account_id/companies/:company_id/contacts` | `api/v1/accounts/companies/contacts#index` | `routes.rb:188` | exact |
| GET | `/api/v1/accounts/:account_id/companies/:company_id/conversations` | `/api/v1/accounts/:account_id/companies/:company_id/conversations` | `api/v1/accounts/companies/conversations#index` | `routes.rb:193` | exact |
| GET | `/api/v1/accounts/:account_id/companies/:company_id/notes` | `/api/v1/accounts/:account_id/companies/:company_id/notes` | `api/v1/accounts/companies/notes#index` | `routes.rb:194` | exact |
| GET | `/api/v1/accounts/:account_id/companies/search` | `/api/v1/accounts/:account_id/companies/search` | `api/v1/accounts/companies#search` | `routes.rb:181` | exact |
| GET | `/api/v1/accounts/:account_id/contacts/` | `/api/v1/accounts/:account_id/contacts/` | `api/v1/accounts/contacts#index` | `routes.rb:197` | exact |
| GET | `/api/v1/accounts/:account_id/contacts/:contact_id` | `/api/v1/accounts/:account_id/contacts/:contact_id` | `api/v1/accounts/contacts#show` | `routes.rb:197` | exact |
@@ -67,13 +83,25 @@ These routes exist with equivalent method and path shape but different parameter
| GET | `/api/v1/accounts/:account_id/conversations/unread_counts` | `/api/v1/accounts/:account_id/conversations/unread_counts` | `api/v1/accounts/conversations/unread_counts#index` | `routes.rb:138` | exact |
| GET | `/api/v1/accounts/:account_id/csat_survey_responses/` | `/api/v1/accounts/:account_id/csat_survey_responses/` | `api/v1/accounts/csat_survey_responses#index` | `routes.rb:219` | exact |
| GET | `/api/v1/accounts/:account_id/csat_survey_responses/metrics` | `/api/v1/accounts/:account_id/csat_survey_responses/metrics` | `api/v1/accounts/csat_survey_responses#metrics` | `routes.rb:221` | exact |
| GET | `/api/v1/accounts/:account_id/custom_attribute_definitions/` | `/api/v1/accounts/:account_id/custom_attribute_definitions/` | `api/v1/accounts/custom_attribute_definitions#index` | `routes.rb:250` | exact |
| GET | `/api/v1/accounts/:account_id/custom_attribute_definitions/:id` | `/api/v1/accounts/:account_id/custom_attribute_definitions/:id` | `api/v1/accounts/custom_attribute_definitions#show` | `routes.rb:250` | exact |
| GET | `/api/v1/accounts/:account_id/custom_filters/` | `/api/v1/accounts/:account_id/custom_filters/` | `api/v1/accounts/custom_filters#index` | `routes.rb:251` | exact |
| GET | `/api/v1/accounts/:account_id/custom_filters/:id` | `/api/v1/accounts/:account_id/custom_filters/:id` | `api/v1/accounts/custom_filters#show` | `routes.rb:251` | exact |
| GET | `/api/v1/accounts/:account_id/custom_roles/` | `/api/v1/accounts/:account_id/custom_roles/` | `api/v1/accounts/custom_roles#index` | `routes.rb:122` | exact |
| GET | `/api/v1/accounts/:account_id/inbox_members/:inbox_id` | `/api/v1/accounts/:account_id/inbox_members/:inbox_id` | `api/v1/accounts/inbox_members#show` | `routes.rb:275` | exact |
| GET | `/api/v1/accounts/:account_id/inboxes/` | `/api/v1/accounts/:account_id/inboxes/` | `api/v1/accounts/inboxes#index` | `routes.rb:252` | exact |
| GET | `/api/v1/accounts/:account_id/inboxes/:inbox_id` | `/api/v1/accounts/:account_id/inboxes/:inbox_id` | `api/v1/accounts/inboxes#show` | `routes.rb:252` | exact |
| GET | `/api/v1/accounts/:account_id/inboxes/:inbox_id/agent_bot` | `/api/v1/accounts/:account_id/inboxes/:inbox_id/agent_bot` | `api/v1/accounts/inboxes#agent_bot` | `routes.rb:255` | exact |
| GET | `/api/v1/accounts/:account_id/inboxes/:inbox_id/assignable_agents` | `/api/v1/accounts/:account_id/inboxes/:inbox_id/assignable_agents` | `api/v1/accounts/inboxes#assignable_agents` | `routes.rb:253` | exact |
| GET | `/api/v1/accounts/:account_id/inboxes/:inbox_id/campaigns` | `/api/v1/accounts/:account_id/inboxes/:inbox_id/campaigns` | `api/v1/accounts/inboxes#campaigns` | `routes.rb:254` | exact |
| GET | `/api/v1/accounts/:account_id/inboxes/:inbox_id/health` | `/api/v1/accounts/:account_id/inboxes/:inbox_id/health` | `api/v1/accounts/inboxes#health` | `routes.rb:259` | exact |
| GET | `/api/v1/accounts/:account_id/labels/` | `/api/v1/accounts/:account_id/labels/` | `api/v1/accounts/labels#index` | `routes.rb:281` | exact |
| GET | `/api/v1/accounts/:account_id/labels/:tag_id` | `/api/v1/accounts/:account_id/labels/:tag_id` | `api/v1/accounts/labels#show` | `routes.rb:281` | exact |
| GET | `/api/v1/accounts/:account_id/macros/` | `/api/v1/accounts/:account_id/macros/` | `api/v1/accounts/macros#index` | `routes.rb:118` | exact |
| GET | `/api/v1/accounts/:account_id/macros/:macro_id` | `/api/v1/accounts/:account_id/macros/:macro_id` | `api/v1/accounts/macros#show` | `routes.rb:118` | exact |
| GET | `/api/v1/accounts/:account_id/notification_settings/` | `/api/v1/accounts/:account_id/notification_settings/` | `api/v1/accounts/notification_settings#show` | `routes.rb:293` | exact |
| GET | `/api/v1/accounts/:account_id/notifications/` | `/api/v1/accounts/:account_id/notifications/` | `api/v1/accounts/notifications#index` | `routes.rb:283` | exact |
| GET | `/api/v1/accounts/:account_id/notifications/unread_count` | `/api/v1/accounts/:account_id/notifications/unread_count` | `api/v1/accounts/notifications#unread_count` | `routes.rb:286` | exact |
| GET | `/api/v1/accounts/:account_id/search` | `/api/v1/accounts/:account_id/search` | `api/v1/accounts/search#index` | `routes.rb:170` | exact |
| GET | `/api/v1/accounts/:account_id/search/articles` | `/api/v1/accounts/:account_id/search/articles` | `api/v1/accounts/search#articles` | `routes.rb:176` | exact |
| GET | `/api/v1/accounts/:account_id/search/contacts` | `/api/v1/accounts/:account_id/search/contacts` | `api/v1/accounts/search#contacts` | `routes.rb:175` | exact |
@@ -81,9 +109,17 @@ These routes exist with equivalent method and path shape but different parameter
| GET | `/api/v1/accounts/:account_id/search/messages` | `/api/v1/accounts/:account_id/search/messages` | `api/v1/accounts/search#messages` | `routes.rb:174` | exact |
| GET | `/api/v1/accounts/:account_id/sla_policies` | `/api/v1/accounts/:account_id/sla_policies` | `api/v1/accounts/sla_policies#index` | `routes.rb:121` | exact |
| GET | `/api/v1/accounts/:account_id/sla_policies/:id` | `/api/v1/accounts/:account_id/sla_policies/:id` | `api/v1/accounts/sla_policies#show` | `routes.rb:121` | exact |
| GET | `/api/v1/accounts/:account_id/teams/` | `/api/v1/accounts/:account_id/teams/` | `api/v1/accounts/teams#index` | `routes.rb:296` | exact |
| GET | `/api/v1/accounts/:account_id/teams/:team_id` | `/api/v1/accounts/:account_id/teams/:team_id` | `api/v1/accounts/teams#show` | `routes.rb:296` | exact |
| GET | `/api/v1/accounts/:account_id/teams/:team_id/team_members/` | `/api/v1/accounts/:account_id/teams/:team_id/team_members/` | `api/v1/accounts/teams/team_members#index` | `routes.rb:297` | exact |
| PATCH | `/api/v1/accounts/:account_id/inbox_members/` | `/api/v1/accounts/:account_id/inbox_members/` | `api/v1/accounts/inbox_members#update` | `routes.rb:278` | exact |
| PATCH | `/api/v1/accounts/:account_id/teams/:team_id/team_members/` | `/api/v1/accounts/:account_id/teams/:team_id/team_members/` | `api/v1/accounts/teams/team_members#update` | `routes.rb:300` | exact |
| POST | `/api/v1/accounts/` | `/api/v1/accounts/` | `api/v1/accounts#create` | `routes.rb:47` | exact |
| POST | `/api/v1/accounts/:account_id/agents` | `/api/v1/accounts/:account_id/agents` | `api/v1/accounts/agents#create` | `routes.rb:59` | exact |
| POST | `/api/v1/accounts/:account_id/agents/bulk_create` | `/api/v1/accounts/:account_id/agents/bulk_create` | `api/v1/accounts/agents#bulk_create` | `routes.rb:60` | exact |
| POST | `/api/v1/accounts/:account_id/automation_rules/` | `/api/v1/accounts/:account_id/automation_rules/` | `api/v1/accounts/automation_rules#create` | `routes.rb:115` | exact |
| POST | `/api/v1/accounts/:account_id/automation_rules/:automation_id/clone` | `/api/v1/accounts/:account_id/automation_rules/:automation_id/clone` | `api/v1/accounts/automation_rules#clone` | `routes.rb:116` | exact |
| POST | `/api/v1/accounts/:account_id/canned_responses/` | `/api/v1/accounts/:account_id/canned_responses/` | `api/v1/accounts/canned_responses#create` | `routes.rb:114` | exact |
| POST | `/api/v1/accounts/:account_id/companies/` | `/api/v1/accounts/:account_id/companies/` | `api/v1/accounts/companies#create` | `routes.rb:179` | exact |
| POST | `/api/v1/accounts/:account_id/companies/:company_id/contacts/:contact_id` | `/api/v1/accounts/:account_id/companies/:company_id/contacts/:contact_id` | `api/v1/accounts/companies/contacts#create` | `routes.rb:188` | exact |
| POST | `/api/v1/accounts/:account_id/contacts/` | `/api/v1/accounts/:account_id/contacts/` | `api/v1/accounts/contacts#create` | `routes.rb:197` | exact |
@@ -101,18 +137,39 @@ These routes exist with equivalent method and path shape but different parameter
| POST | `/api/v1/accounts/:account_id/conversations/:conversation_id/toggle_status` | `/api/v1/accounts/:account_id/conversations/:conversation_id/toggle_status` | `api/v1/accounts/conversations#toggle_status` | `routes.rb:158` | exact |
| POST | `/api/v1/accounts/:account_id/conversations/:conversation_id/transcript` | `/api/v1/accounts/:account_id/conversations/:conversation_id/transcript` | `api/v1/accounts/conversations#transcript` | `routes.rb:157` | exact |
| POST | `/api/v1/accounts/:account_id/conversations/filter` | `/api/v1/accounts/:account_id/conversations/filter` | `api/v1/accounts/conversations#filter` | `routes.rb:139` | exact |
| POST | `/api/v1/accounts/:account_id/custom_attribute_definitions/` | `/api/v1/accounts/:account_id/custom_attribute_definitions/` | `api/v1/accounts/custom_attribute_definitions#create` | `routes.rb:250` | exact |
| POST | `/api/v1/accounts/:account_id/custom_filters/` | `/api/v1/accounts/:account_id/custom_filters/` | `api/v1/accounts/custom_filters#create` | `routes.rb:251` | exact |
| POST | `/api/v1/accounts/:account_id/inbox_members/` | `/api/v1/accounts/:account_id/inbox_members/` | `api/v1/accounts/inbox_members#create` | `routes.rb:275` | exact |
| POST | `/api/v1/accounts/:account_id/inboxes/` | `/api/v1/accounts/:account_id/inboxes/` | `api/v1/accounts/inboxes#create` | `routes.rb:252` | exact |
| POST | `/api/v1/accounts/:account_id/inboxes/:inbox_id/register_webhook` | `/api/v1/accounts/:account_id/inboxes/:inbox_id/register_webhook` | `api/v1/accounts/inboxes#register_webhook` | `routes.rb:260` | exact |
| POST | `/api/v1/accounts/:account_id/inboxes/:inbox_id/reset_secret` | `/api/v1/accounts/:account_id/inboxes/:inbox_id/reset_secret` | `api/v1/accounts/inboxes#reset_secret` | `routes.rb:262` | exact |
| POST | `/api/v1/accounts/:account_id/inboxes/:inbox_id/set_agent_bot` | `/api/v1/accounts/:account_id/inboxes/:inbox_id/set_agent_bot` | `api/v1/accounts/inboxes#set_agent_bot` | `routes.rb:256` | exact |
| POST | `/api/v1/accounts/:account_id/inboxes/:inbox_id/sync_templates` | `/api/v1/accounts/:account_id/inboxes/:inbox_id/sync_templates` | `api/v1/accounts/inboxes#sync_templates` | `routes.rb:258` | exact |
| POST | `/api/v1/accounts/:account_id/labels/` | `/api/v1/accounts/:account_id/labels/` | `api/v1/accounts/labels#create` | `routes.rb:281` | exact |
| POST | `/api/v1/accounts/:account_id/macros/` | `/api/v1/accounts/:account_id/macros/` | `api/v1/accounts/macros#create` | `routes.rb:118` | exact |
| POST | `/api/v1/accounts/:account_id/macros/:macro_id/execute` | `/api/v1/accounts/:account_id/macros/:macro_id/execute` | `api/v1/accounts/macros#execute` | `routes.rb:119` | exact |
| POST | `/api/v1/accounts/:account_id/notifications/:notification_id/snooze` | `/api/v1/accounts/:account_id/notifications/:notification_id/snooze` | `api/v1/accounts/notifications#snooze` | `routes.rb:290` | exact |
| POST | `/api/v1/accounts/:account_id/notifications/:notification_id/unread` | `/api/v1/accounts/:account_id/notifications/:notification_id/unread` | `api/v1/accounts/notifications#unread` | `routes.rb:291` | exact |
| POST | `/api/v1/accounts/:account_id/notifications/destroy_all` | `/api/v1/accounts/:account_id/notifications/destroy_all` | `api/v1/accounts/notifications#destroy_all` | `routes.rb:287` | exact |
| POST | `/api/v1/accounts/:account_id/notifications/read_all` | `/api/v1/accounts/:account_id/notifications/read_all` | `api/v1/accounts/notifications#read_all` | `routes.rb:285` | exact |
| POST | `/api/v1/accounts/:account_id/sla_policies` | `/api/v1/accounts/:account_id/sla_policies` | `api/v1/accounts/sla_policies#create` | `routes.rb:121` | exact |
| POST | `/api/v1/accounts/:account_id/teams/` | `/api/v1/accounts/:account_id/teams/` | `api/v1/accounts/teams#create` | `routes.rb:296` | exact |
| POST | `/api/v1/accounts/:account_id/teams/:team_id/team_members/` | `/api/v1/accounts/:account_id/teams/:team_id/team_members/` | `api/v1/accounts/teams/team_members#create` | `routes.rb:297` | exact |
| 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 |
| PUT | `/api/v1/accounts/:account_id` | `/api/v1/accounts/:account_id` | `api/v1/accounts#update` | `routes.rb:47` | exact |
| PUT | `/api/v1/accounts/:account_id/agents/:agent_id` | `/api/v1/accounts/:account_id/agents/:agent_id` | `api/v1/accounts/agents#update` | `routes.rb:59` | exact |
| PUT | `/api/v1/accounts/:account_id/automation_rules/:automation_id` | `/api/v1/accounts/:account_id/automation_rules/:automation_id` | `api/v1/accounts/automation_rules#update` | `routes.rb:115` | exact |
| PUT | `/api/v1/accounts/:account_id/canned_responses/:id` | `/api/v1/accounts/:account_id/canned_responses/:id` | `api/v1/accounts/canned_responses#update` | `routes.rb:114` | exact |
| PUT | `/api/v1/accounts/:account_id/companies/:company_id` | `/api/v1/accounts/:account_id/companies/:company_id` | `api/v1/accounts/companies#update` | `routes.rb:179` | exact |
| PUT | `/api/v1/accounts/:account_id/contacts/:contact_id` | `/api/v1/accounts/:account_id/contacts/:contact_id` | `api/v1/accounts/contacts#update` | `routes.rb:197` | exact |
| PUT | `/api/v1/accounts/:account_id/conversations/:conversation_id` | `/api/v1/accounts/:account_id/conversations/:conversation_id` | `api/v1/accounts/conversations#update` | `routes.rb:134` | exact |
| PUT | `/api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id` | `/api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id` | `api/v1/accounts/conversations/messages#update` | `routes.rb:142` | exact |
| PUT | `/api/v1/accounts/:account_id/custom_attribute_definitions/:id` | `/api/v1/accounts/:account_id/custom_attribute_definitions/:id` | `api/v1/accounts/custom_attribute_definitions#update` | `routes.rb:250` | exact |
| PUT | `/api/v1/accounts/:account_id/custom_filters/:id` | `/api/v1/accounts/:account_id/custom_filters/:id` | `api/v1/accounts/custom_filters#update` | `routes.rb:251` | exact |
| PUT | `/api/v1/accounts/:account_id/inboxes/:inbox_id` | `/api/v1/accounts/:account_id/inboxes/:inbox_id` | `api/v1/accounts/inboxes#update` | `routes.rb:252` | exact |
| PUT | `/api/v1/accounts/:account_id/labels/:tag_id` | `/api/v1/accounts/:account_id/labels/:tag_id` | `api/v1/accounts/labels#update` | `routes.rb:281` | exact |
| PUT | `/api/v1/accounts/:account_id/macros/:macro_id` | `/api/v1/accounts/:account_id/macros/:macro_id` | `api/v1/accounts/macros#update` | `routes.rb:118` | exact |
| PUT | `/api/v1/accounts/:account_id/notification_settings/` | `/api/v1/accounts/:account_id/notification_settings/` | `api/v1/accounts/notification_settings#update` | `routes.rb:293` | exact |
| PUT | `/api/v1/accounts/:account_id/notifications/:notification_id` | `/api/v1/accounts/:account_id/notifications/:notification_id` | `api/v1/accounts/notifications#update` | `routes.rb:283` | exact |
| PUT | `/api/v1/accounts/:account_id/sla_policies/:id` | `/api/v1/accounts/:account_id/sla_policies/:id` | `api/v1/accounts/sla_policies#update` | `routes.rb:121` | exact |
| PUT | `/api/v1/accounts/:account_id/teams/:team_id` | `/api/v1/accounts/:account_id/teams/:team_id` | `api/v1/accounts/teams#update` | `routes.rb:296` | exact |
@@ -151,4 +151,72 @@ func (h *InboxMemberHandler) UpdateMultiple(c *gin.Context) {
"members": members,
"meta": gin.H{"count": len(members)},
})
}
}
// ShowAccountScoped retrieves all agents assigned to an inbox using Chatwoot's account-level route.
// GET /api/v1/accounts/:account_id/inbox_members/:inbox_id
func (h *InboxMemberHandler) ShowAccountScoped(c *gin.Context) {
inboxID, err := parseUintParam(c, "inbox_id")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid inbox id"})
return
}
members, svcErr := h.svc.ListByInbox(c.Request.Context(), inboxID)
if svcErr != nil {
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "failed to list inbox members"})
return
}
c.JSON(http.StatusOK, gin.H{"members": members, "meta": gin.H{"count": len(members)}})
}
// CreateAccountScoped adds or replaces inbox members using Chatwoot's account-level create route.
// POST /api/v1/accounts/:account_id/inbox_members
func (h *InboxMemberHandler) CreateAccountScoped(c *gin.Context) {
h.updateAccountScoped(c)
}
// UpdateAccountScoped replaces all members of an inbox using Chatwoot's account-level update route.
// PATCH /api/v1/accounts/:account_id/inbox_members
func (h *InboxMemberHandler) UpdateAccountScoped(c *gin.Context) {
h.updateAccountScoped(c)
}
func (h *InboxMemberHandler) updateAccountScoped(c *gin.Context) {
var req service.UpdateMultipleRequest
if bindErr := c.ShouldBindJSON(&req); bindErr != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": bindErr.Error()})
return
}
members, svcErr := h.svc.UpdateMultiple(c.Request.Context(), req)
if svcErr != nil {
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "failed to update members"})
return
}
c.JSON(http.StatusOK, gin.H{"members": members, "meta": gin.H{"count": len(members)}})
}
// DestroyAccountScoped removes selected users from an inbox using Chatwoot's account-level route.
// DELETE /api/v1/accounts/:account_id/inbox_members
func (h *InboxMemberHandler) DestroyAccountScoped(c *gin.Context) {
var req struct {
InboxID uint `json:"inbox_id" binding:"required"`
UserIDs []uint `json:"user_ids" binding:"required"`
}
if bindErr := c.ShouldBindJSON(&req); bindErr != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": bindErr.Error()})
return
}
for _, userID := range req.UserIDs {
if svcErr := h.svc.RemoveMember(c.Request.Context(), req.InboxID, userID); svcErr != nil {
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "failed to remove member"})
return
}
}
c.Status(http.StatusOK)
}
@@ -57,7 +57,7 @@ func (h *NotificationHandler) Get(c *gin.Context) {
response.AbortWithStatusError(c, http.StatusServiceUnavailable, response.ErrInternal, "Notification service not available")
return
}
notificationID, err := parseUintParam(c, "notification_id")
notificationID, err := parseUintAnyParam(c, "notification_id", "id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid notification id")
return
@@ -76,7 +76,7 @@ func (h *NotificationHandler) Get(c *gin.Context) {
// PUT /api/v1/accounts/:account_id/notifications/:id
// Reference: Chatwoot update — @notification.update(read_at: DateTime.now.utc); render json: @notification
func (h *NotificationHandler) Update(c *gin.Context) {
notificationID, err := parseUintParam(c, "notification_id")
notificationID, err := parseUintAnyParam(c, "notification_id", "id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid notification id")
return
@@ -134,7 +134,7 @@ func (h *NotificationHandler) UnreadCount(c *gin.Context) {
// POST /api/v1/accounts/:account_id/notifications/:id/snooze
// Reference: Chatwoot snooze — update(snoozed_until: ..., meta: ...)
func (h *NotificationHandler) Snooze(c *gin.Context) {
notificationID, err := parseUintParam(c, "notification_id")
notificationID, err := parseUintAnyParam(c, "notification_id", "id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid notification id")
return
@@ -170,7 +170,7 @@ func (h *NotificationHandler) Snooze(c *gin.Context) {
// POST /api/v1/accounts/:account_id/notifications/:id/unread
// Reference: Chatwoot unread — @notification.update(read_at: nil); render json: @notification
func (h *NotificationHandler) Unread(c *gin.Context) {
notificationID, err := parseUintParam(c, "notification_id")
notificationID, err := parseUintAnyParam(c, "notification_id", "id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid notification id")
return
@@ -192,7 +192,7 @@ func (h *NotificationHandler) Unread(c *gin.Context) {
// DELETE /api/v1/accounts/:account_id/notifications/:id
// Reference: Chatwoot destroy — @notification.destroy; head :ok
func (h *NotificationHandler) Destroy(c *gin.Context) {
notificationID, err := parseUintParam(c, "notification_id")
notificationID, err := parseUintAnyParam(c, "notification_id", "id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid notification id")
return
@@ -225,4 +225,4 @@ func (h *NotificationHandler) DestroyAll(c *gin.Context) {
}
c.Status(http.StatusOK)
}
}
+22 -5
View File
@@ -519,13 +519,13 @@ func registerV1Routes(g *gin.RouterGroup, h *Handlers) {
inboxes.PATCH("/:inbox_id/members/update_multiple", h.InboxMember.UpdateMultiple)
inboxes.DELETE("/:inbox_id/members/:user_id", h.InboxMember.RemoveMember)
// Account-level inbox_members (Chatwoot: resources :inbox_members)
// POST /inbox_members (create), DELETE /inbox_members/:id (destroy)
// These are account-scoped routes for managing inbox assignments
// Account-level inbox_members (Chatwoot: resources :inbox_members, param: :inbox_id)
inboxMembers := accountScoped.Group("/inbox_members")
{
inboxMembers.POST("/", h.InboxMember.AddMember)
inboxMembers.DELETE("/:user_id", h.InboxMember.RemoveMember)
inboxMembers.POST("/", h.InboxMember.CreateAccountScoped)
inboxMembers.PATCH("/", h.InboxMember.UpdateAccountScoped)
inboxMembers.DELETE("/", h.InboxMember.DestroyAccountScoped)
inboxMembers.GET("/:inbox_id", h.InboxMember.ShowAccountScoped)
}
// CSAT template (ref: Chatwoot resource :csat_template, singular per inbox)
@@ -1338,6 +1338,23 @@ func registerV1Routes(g *gin.RouterGroup, h *Handlers) {
{
notifSettings.GET("/", h.NotificationSetting.Show)
notifSettings.PATCH("/", h.NotificationSetting.Update)
notifSettings.PUT("/", h.NotificationSetting.Update)
}
// Notifications — account-scoped Chatwoot routes used by the dashboard frontend.
notifications := accountScoped.Group("/notifications")
{
notifications.GET("/", h.Notification.List)
notifications.POST("/read_all", h.Notification.MarkAllRead)
notifications.GET("/unread_count", h.Notification.UnreadCount)
notifications.POST("/destroy_all", h.Notification.DestroyAll)
notifications.DELETE("/destroy_all", h.Notification.DestroyAll)
notifications.GET("/:notification_id", h.Notification.Get)
notifications.PUT("/:notification_id", h.Notification.Update)
notifications.PATCH("/:notification_id", h.Notification.Update)
notifications.DELETE("/:notification_id", h.Notification.Destroy)
notifications.POST("/:notification_id/snooze", h.Notification.Snooze)
notifications.POST("/:notification_id/unread", h.Notification.Unread)
}
// Notification subscriptions (account-scoped)