diff --git a/.gitignore b/.gitignore index 992347da..4a51a63d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ tmp/ # IDE .idea/ .vscode/ +.codegraph/ *.swp *.swo diff --git a/cmd/route_parity/main.go b/cmd/route_parity/main.go new file mode 100644 index 00000000..dfb07a6a --- /dev/null +++ b/cmd/route_parity/main.go @@ -0,0 +1,330 @@ +package main + +import ( + "bufio" + "flag" + "fmt" + "os" + "regexp" + "sort" + "strings" +) + +type route struct { + Method string + Path string + Controller string + Source string +} + +type sourceDecl struct { + Line int + Text string +} + +var criticalRoutes = []route{ + {Method: "POST", Path: "/api/v1/accounts/", Controller: "api/v1/accounts#create", Source: "routes.rb:47"}, + {Method: "GET", Path: "/api/v1/accounts/:account_id", Controller: "api/v1/accounts#show", Source: "routes.rb:47"}, + {Method: "PUT", Path: "/api/v1/accounts/:account_id", Controller: "api/v1/accounts#update", Source: "routes.rb:47"}, + {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/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"}, + {Method: "PUT", Path: "/api/v1/accounts/:account_id/conversations/:conversation_id", Controller: "api/v1/accounts/conversations#update", Source: "routes.rb:134"}, + {Method: "DELETE", Path: "/api/v1/accounts/:account_id/conversations/:conversation_id", Controller: "api/v1/accounts/conversations#destroy", Source: "routes.rb:134"}, + {Method: "GET", Path: "/api/v1/accounts/:account_id/conversations/meta", Controller: "api/v1/accounts/conversations#meta", Source: "routes.rb:136"}, + {Method: "GET", Path: "/api/v1/accounts/:account_id/conversations/search", Controller: "api/v1/accounts/conversations#search", Source: "routes.rb:137"}, + {Method: "GET", Path: "/api/v1/accounts/:account_id/conversations/unread_counts", Controller: "api/v1/accounts/conversations/unread_counts#index", Source: "routes.rb:138"}, + {Method: "POST", Path: "/api/v1/accounts/:account_id/conversations/filter", Controller: "api/v1/accounts/conversations#filter", Source: "routes.rb:139"}, + {Method: "POST", Path: "/api/v1/accounts/:account_id/conversations/:conversation_id/assignments", Controller: "api/v1/accounts/conversations/assignments#create", Source: "routes.rb:148"}, + {Method: "POST", Path: "/api/v1/accounts/:account_id/conversations/:conversation_id/labels", Controller: "api/v1/accounts/conversations/labels#create", Source: "routes.rb:149"}, + {Method: "GET", Path: "/api/v1/accounts/:account_id/conversations/:conversation_id/labels", Controller: "api/v1/accounts/conversations/labels#index", Source: "routes.rb:149"}, + {Method: "POST", Path: "/api/v1/accounts/:account_id/conversations/:conversation_id/toggle_status", Controller: "api/v1/accounts/conversations#toggle_status", Source: "routes.rb:158"}, + {Method: "POST", Path: "/api/v1/accounts/:account_id/conversations/:conversation_id/toggle_priority", Controller: "api/v1/accounts/conversations#toggle_priority", Source: "routes.rb:159"}, + {Method: "POST", Path: "/api/v1/accounts/:account_id/conversations/:conversation_id/transcript", Controller: "api/v1/accounts/conversations#transcript", Source: "routes.rb:157"}, + + {Method: "GET", Path: "/api/v1/accounts/:account_id/conversations/:conversation_id/messages/", Controller: "api/v1/accounts/conversations/messages#index", Source: "routes.rb:142"}, + {Method: "POST", Path: "/api/v1/accounts/:account_id/conversations/:conversation_id/messages/", Controller: "api/v1/accounts/conversations/messages#create", Source: "routes.rb:142"}, + {Method: "PUT", Path: "/api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id", Controller: "api/v1/accounts/conversations/messages#update", Source: "routes.rb:142"}, + {Method: "DELETE", Path: "/api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id", Controller: "api/v1/accounts/conversations/messages#destroy", Source: "routes.rb:142"}, + {Method: "POST", Path: "/api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id/translate", Controller: "api/v1/accounts/conversations/messages#translate", Source: "routes.rb:144"}, + {Method: "POST", Path: "/api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id/retry", Controller: "api/v1/accounts/conversations/messages#retry", Source: "routes.rb:145"}, + + {Method: "GET", Path: "/api/v1/accounts/:account_id/contacts/", Controller: "api/v1/accounts/contacts#index", Source: "routes.rb:197"}, + {Method: "POST", Path: "/api/v1/accounts/:account_id/contacts/", Controller: "api/v1/accounts/contacts#create", Source: "routes.rb:197"}, + {Method: "GET", Path: "/api/v1/accounts/:account_id/contacts/:contact_id", Controller: "api/v1/accounts/contacts#show", Source: "routes.rb:197"}, + {Method: "PUT", Path: "/api/v1/accounts/:account_id/contacts/:contact_id", Controller: "api/v1/accounts/contacts#update", Source: "routes.rb:197"}, + {Method: "DELETE", Path: "/api/v1/accounts/:account_id/contacts/:contact_id", Controller: "api/v1/accounts/contacts#destroy", Source: "routes.rb:197"}, + {Method: "GET", Path: "/api/v1/accounts/:account_id/contacts/search", Controller: "api/v1/accounts/contacts#search", Source: "routes.rb:201"}, + {Method: "POST", Path: "/api/v1/accounts/:account_id/contacts/filter", Controller: "api/v1/accounts/contacts#filter", Source: "routes.rb:202"}, + {Method: "POST", Path: "/api/v1/accounts/:account_id/contacts/import", Controller: "api/v1/accounts/contacts#import", Source: "routes.rb:203"}, + {Method: "POST", Path: "/api/v1/accounts/:account_id/contacts/export", Controller: "api/v1/accounts/contacts#export", Source: "routes.rb:204"}, + {Method: "GET", Path: "/api/v1/accounts/:account_id/contacts/:contact_id/conversations", Controller: "api/v1/accounts/contacts/conversations#index", Source: "routes.rb:211"}, + {Method: "GET", Path: "/api/v1/accounts/:account_id/contacts/:contact_id/notes", Controller: "api/v1/accounts/contacts/notes#index", Source: "routes.rb:214"}, + {Method: "POST", Path: "/api/v1/accounts/:account_id/contacts/:contact_id/notes", Controller: "api/v1/accounts/contacts/notes#create", Source: "routes.rb:214"}, + + {Method: "GET", Path: "/api/v1/accounts/:account_id/companies/", Controller: "api/v1/accounts/companies#index", Source: "routes.rb:179"}, + {Method: "POST", Path: "/api/v1/accounts/:account_id/companies/", Controller: "api/v1/accounts/companies#create", Source: "routes.rb:179"}, + {Method: "GET", Path: "/api/v1/accounts/:account_id/companies/:company_id", Controller: "api/v1/accounts/companies#show", Source: "routes.rb:179"}, + {Method: "PUT", Path: "/api/v1/accounts/:account_id/companies/:company_id", Controller: "api/v1/accounts/companies#update", Source: "routes.rb:179"}, + {Method: "DELETE", Path: "/api/v1/accounts/:account_id/companies/:company_id", Controller: "api/v1/accounts/companies#destroy", Source: "routes.rb:179"}, + {Method: "GET", Path: "/api/v1/accounts/:account_id/companies/search", Controller: "api/v1/accounts/companies#search", Source: "routes.rb:181"}, + {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/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"}, + {Method: "GET", Path: "/api/v1/accounts/:account_id/search/messages", Controller: "api/v1/accounts/search#messages", Source: "routes.rb:174"}, + {Method: "GET", Path: "/api/v1/accounts/:account_id/search/contacts", Controller: "api/v1/accounts/search#contacts", Source: "routes.rb:175"}, + {Method: "GET", Path: "/api/v1/accounts/:account_id/search/articles", Controller: "api/v1/accounts/search#articles", Source: "routes.rb:176"}, + + {Method: "GET", Path: "/api/v1/accounts/:account_id/inboxes/", Controller: "api/v1/accounts/inboxes#index", Source: "routes.rb:252"}, + {Method: "POST", Path: "/api/v1/accounts/:account_id/inboxes/", Controller: "api/v1/accounts/inboxes#create", Source: "routes.rb:252"}, + {Method: "GET", Path: "/api/v1/accounts/:account_id/inboxes/:inbox_id", Controller: "api/v1/accounts/inboxes#show", Source: "routes.rb:252"}, + {Method: "PUT", Path: "/api/v1/accounts/:account_id/inboxes/:inbox_id", Controller: "api/v1/accounts/inboxes#update", Source: "routes.rb:252"}, + {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: "POST", Path: "/api/v1/accounts/:account_id/inboxes/:inbox_id/reset_secret", Controller: "api/v1/accounts/inboxes#reset_secret", Source: "routes.rb:262"}, + + {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"}, + {Method: "PUT", Path: "/api/v1/accounts/:account_id/automation_rules/:automation_id", Controller: "api/v1/accounts/automation_rules#update", Source: "routes.rb:115"}, + {Method: "DELETE", Path: "/api/v1/accounts/:account_id/automation_rules/:automation_id", Controller: "api/v1/accounts/automation_rules#destroy", Source: "routes.rb:115"}, + {Method: "POST", Path: "/api/v1/accounts/:account_id/automation_rules/:automation_id/clone", Controller: "api/v1/accounts/automation_rules#clone", Source: "routes.rb:116"}, + + {Method: "GET", Path: "/api/v1/accounts/:account_id/macros/", Controller: "api/v1/accounts/macros#index", Source: "routes.rb:118"}, + {Method: "POST", Path: "/api/v1/accounts/:account_id/macros/", Controller: "api/v1/accounts/macros#create", Source: "routes.rb:118"}, + {Method: "GET", Path: "/api/v1/accounts/:account_id/macros/:macro_id", Controller: "api/v1/accounts/macros#show", Source: "routes.rb:118"}, + {Method: "PUT", Path: "/api/v1/accounts/:account_id/macros/:macro_id", Controller: "api/v1/accounts/macros#update", Source: "routes.rb:118"}, + {Method: "DELETE", Path: "/api/v1/accounts/:account_id/macros/:macro_id", Controller: "api/v1/accounts/macros#destroy", Source: "routes.rb:118"}, + {Method: "POST", Path: "/api/v1/accounts/:account_id/macros/:macro_id/execute", Controller: "api/v1/accounts/macros#execute", Source: "routes.rb:119"}, + + {Method: "GET", Path: "/api/v1/accounts/:account_id/sla_policies", Controller: "api/v1/accounts/sla_policies#index", Source: "routes.rb:121"}, + {Method: "POST", Path: "/api/v1/accounts/:account_id/sla_policies", Controller: "api/v1/accounts/sla_policies#create", Source: "routes.rb:121"}, + {Method: "GET", Path: "/api/v1/accounts/:account_id/sla_policies/:id", Controller: "api/v1/accounts/sla_policies#show", Source: "routes.rb:121"}, + {Method: "PUT", Path: "/api/v1/accounts/:account_id/sla_policies/:id", Controller: "api/v1/accounts/sla_policies#update", Source: "routes.rb:121"}, + {Method: "DELETE", Path: "/api/v1/accounts/:account_id/sla_policies/:id", Controller: "api/v1/accounts/sla_policies#destroy", Source: "routes.rb:121"}, + {Method: "GET", Path: "/api/v1/accounts/:account_id/custom_roles/", Controller: "api/v1/accounts/custom_roles#index", Source: "routes.rb:122"}, + {Method: "GET", Path: "/api/v1/accounts/:account_id/agent_capacity_policies/", Controller: "api/v1/accounts/agent_capacity_policies#index", Source: "routes.rb:123"}, + {Method: "GET", Path: "/api/v1/accounts/:account_id/csat_survey_responses/", Controller: "api/v1/accounts/csat_survey_responses#index", Source: "routes.rb:219"}, + {Method: "GET", Path: "/api/v1/accounts/:account_id/csat_survey_responses/metrics", Controller: "api/v1/accounts/csat_survey_responses#metrics", Source: "routes.rb:221"}, +} + +func main() { + gochatPath := flag.String("gochat", "docs/parity/gochat_routes.txt", "GoChat route dump") + chatwootPath := flag.String("chatwoot", "reference/chatwoot/config/routes.rb", "Chatwoot routes.rb path") + outPath := flag.String("out", "docs/parity/route_parity.md", "route parity markdown output") + chatwootOut := flag.String("chatwoot-out", "docs/parity/chatwoot_routes_static.md", "Chatwoot static route source output") + flag.Parse() + + gochatRoutes, err := readRouteDump(*gochatPath) + fatalIf(err) + decls, err := extractChatwootDecls(*chatwootPath) + fatalIf(err) + fatalIf(writeChatwootSource(*chatwootOut, *chatwootPath, decls)) + fatalIf(writeParity(*outPath, *gochatPath, *chatwootPath, gochatRoutes)) +} + +func readRouteDump(path string) (map[string]route, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + + routes := map[string]route{} + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if line == "" || strings.HasPrefix(line, "TOTAL:") { + continue + } + parts := strings.Fields(line) + if len(parts) < 2 { + continue + } + r := route{Method: parts[0], Path: parts[1]} + routes[key(r.Method, r.Path)] = r + } + return routes, scanner.Err() +} + +func extractChatwootDecls(path string) ([]sourceDecl, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + + interesting := regexp.MustCompile(`^\s*(namespace|scope|resources?|get|post|put|patch|delete|mount|mount_devise_token_auth_for)\b`) + var out []sourceDecl + scanner := bufio.NewScanner(f) + lineNo := 0 + for scanner.Scan() { + lineNo++ + line := scanner.Text() + trimmed := strings.TrimSpace(line) + if trimmed == "" || strings.HasPrefix(trimmed, "#") { + continue + } + if interesting.MatchString(line) { + out = append(out, sourceDecl{Line: lineNo, Text: trimmed}) + } + } + return out, scanner.Err() +} + +func writeChatwootSource(path string, source string, decls []sourceDecl) error { + var b strings.Builder + b.WriteString("# Chatwoot Route Source Declarations\n\n") + b.WriteString("Source: `" + source + "`\n\n") + b.WriteString("Ruby is unavailable in the current workspace, so this file records static route DSL declarations with source lines. It is a repeatable fallback until `bin/rails routes` can run.\n\n") + b.WriteString("| Line | Declaration |\n") + b.WriteString("| --- | --- |\n") + for _, decl := range decls { + b.WriteString(fmt.Sprintf("| %d | `%s` |\n", decl.Line, escapePipes(decl.Text))) + } + return os.WriteFile(path, []byte(b.String()), 0o644) +} + +func writeParity(path string, gochatPath string, chatwootPath string, gochatRoutes map[string]route) error { + var exact, methodCompatible, compatible, missing []route + for _, expected := range criticalRoutes { + if _, ok := gochatRoutes[key(expected.Method, expected.Path)]; ok { + exact = append(exact, expected) + continue + } + if hasMethodCompatible(gochatRoutes, expected) { + methodCompatible = append(methodCompatible, expected) + continue + } + if hasParamCompatible(gochatRoutes, expected) { + compatible = append(compatible, expected) + continue + } + missing = append(missing, expected) + } + + sortRoutes(exact) + sortRoutes(methodCompatible) + sortRoutes(compatible) + sortRoutes(missing) + + var b strings.Builder + b.WriteString("# Route Parity Report\n\n") + 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(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") + writeRouteTable(&b, missing, gochatRoutes, "missing") + b.WriteString("\n## Method-Compatible Routes\n\n") + b.WriteString("These routes exist at the same path but with a compatible HTTP method. Rails resource `update` routes expose both `PATCH` and `PUT`; GoChat currently exposes only one method for some update endpoints. Keep these tracked until both methods are registered or the frontend is proven to call only the implemented method.\n\n") + writeRouteTable(&b, methodCompatible, gochatRoutes, "method-compatible") + b.WriteString("\n## Parameter-Compatible Routes\n\n") + b.WriteString("These routes exist with equivalent method and path shape but different parameter names. They need handler/serializer parity review, and exact external path compatibility must be preserved.\n\n") + writeRouteTable(&b, compatible, gochatRoutes, "parameter-compatible") + b.WriteString("\n## Exact Critical Routes\n\n") + writeRouteTable(&b, exact, gochatRoutes, "exact") + + return os.WriteFile(path, []byte(b.String()), 0o644) +} + +func writeRouteTable(b *strings.Builder, routes []route, gochatRoutes map[string]route, status string) { + b.WriteString("| Method | Chatwoot Path | GoChat Match | Controller | Source | Status |\n") + b.WriteString("| --- | --- | --- | --- | --- | --- |\n") + if len(routes) == 0 { + b.WriteString("| - | - | - | - | - | none |\n") + return + } + for _, r := range routes { + match := "-" + if status == "exact" { + match = r.Path + } else if status == "method-compatible" { + match = methodCompatibleRoute(gochatRoutes, r) + } else if status == "parameter-compatible" { + match = compatiblePath(gochatRoutes, r) + } + b.WriteString(fmt.Sprintf("| %s | `%s` | `%s` | `%s` | `%s` | %s |\n", r.Method, r.Path, match, r.Controller, r.Source, status)) + } +} + +func hasMethodCompatible(routes map[string]route, expected route) bool { + return methodCompatibleRoute(routes, expected) != "-" +} + +func methodCompatibleRoute(routes map[string]route, expected route) string { + for _, method := range compatibleMethods(expected.Method) { + if _, ok := routes[key(method, expected.Path)]; ok { + return method + " " + expected.Path + } + } + return "-" +} + +func compatibleMethods(method string) []string { + switch method { + case "PUT": + return []string{"PATCH"} + case "PATCH": + return []string{"PUT"} + default: + return nil + } +} + +func hasParamCompatible(routes map[string]route, expected route) bool { + return compatiblePath(routes, expected) != "-" +} + +func compatiblePath(routes map[string]route, expected route) string { + normExpected := normalizeParams(expected.Path) + for _, r := range routes { + if r.Method == expected.Method && normalizeParams(r.Path) == normExpected { + return r.Path + } + } + return "-" +} + +func normalizeParams(path string) string { + parts := strings.Split(path, "/") + for i, part := range parts { + if strings.HasPrefix(part, ":") { + parts[i] = ":param" + } + } + return strings.Join(parts, "/") +} + +func key(method, path string) string { + return method + " " + path +} + +func sortRoutes(routes []route) { + sort.Slice(routes, func(i, j int) bool { + if routes[i].Method != routes[j].Method { + return routes[i].Method < routes[j].Method + } + return routes[i].Path < routes[j].Path + }) +} + +func escapePipes(s string) string { + return strings.ReplaceAll(s, "|", "\\|") +} + +func fatalIf(err error) { + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} diff --git a/docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md b/docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md index 02ef8f5e..a9e30d4e 100644 --- a/docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md +++ b/docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md @@ -18,9 +18,24 @@ Build GoChat as a Go backend that can directly reuse the frontend from `referenc - `go test ./...` passes. - Route dump succeeds with `TOTAL: 704`. +- 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: 76 exact, 2 method-compatible, 0 parameter-compatible, 3 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. +## Execution Snapshot + +| Phase | Name | Status | Blocking gaps | +| --- | --- | --- | --- | +| Phase 0 | Test and route baseline | Done | none | +| Phase 1 | Meilisearch search engine | Review | live Meilisearch integration and document-shape parity still need reference verification | +| Phase 2 | Route and controller parity audit | Doing | Ruby/Bundler unavailable, so Chatwoot route extraction currently uses static `routes.rb` fallback | +| Phase 3 | Data and serializer parity | Planned | needs route-gap priorities from Phase 2 | +| Phase 4 | Enterprise feature completion | Planned | excluded SSO family must stay out of scope; all other enterprise features remain included | +| Phase 5 | Background jobs and integrations | Planned | durable worker choice and job parity are open | +| Phase 6 | Core placeholder burn-down | Planned | placeholders must be converted into real Chatwoot-compatible behavior | +| Phase 7 | Verification harness | Planned | route/JSON/frontend smoke harness not complete | + ## Phase 0: Test And Route Baseline Status: done. @@ -44,7 +59,7 @@ env GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go run ./cmd/d ## Phase 1: Meilisearch Search Engine -Status: next. +Status: review. Source material: @@ -54,15 +69,15 @@ Source material: Checklist: -- [ ] Define a stable `SearchEngine` interface for Meilisearch-backed search and indexing. -- [ ] Add search config for engine, host, API key, and index prefix. -- [ ] Implement Meilisearch client wrapper with index bootstrapping and settings. -- [ ] Define per-entity documents for conversations, messages, contacts, companies, articles, and help-center content as required by Chatwoot frontend behavior. -- [ ] Wire create/update/delete hooks from services into indexing. -- [ ] Add batch reindex command for existing data. -- [ ] Keep DB search only as explicit development fallback, not as final production mode. -- [ ] Add tests with a mocked search engine and integration hooks that can run without a live Meilisearch instance. -- [ ] Document required Meilisearch environment variables and local startup flow. +- [x] Define a stable `SearchEngine` interface for Meilisearch-backed search and indexing. +- [x] Add search config for engine, host, API key, and index prefix. +- [x] Implement Meilisearch client wrapper with index bootstrapping and settings. +- [x] Define per-entity documents for conversations, messages, contacts, companies, articles, and help-center content as required by Chatwoot frontend behavior. +- [x] Wire create/update/delete hooks from services into indexing. +- [x] Add batch reindex command for existing data. +- [x] Keep DB search only as explicit development fallback, not as final production mode. +- [x] Add tests with a mocked search engine and integration hooks that can run without a live Meilisearch instance. +- [x] Document required Meilisearch environment variables and local startup flow. Acceptance: @@ -104,14 +119,15 @@ Search environment variables: ## Phase 2: Route And Controller Parity Audit -Status: planned. +Status: doing. Checklist: -- [ ] Dump Chatwoot routes from `reference/chatwoot`. -- [ ] Dump GoChat routes with `cmd/dump_routes`. -- [ ] Build a tracked route parity table covering method, path, controller, auth scope, request params, and response serializer. -- [ ] Prioritize frontend-critical routes used by the Chatwoot web app. +- [x] Dump Chatwoot route source declarations from `reference/chatwoot/config/routes.rb`. +- [x] Dump GoChat routes with `cmd/dump_routes`. +- [x] Build a first tracked route parity table covering method, path, controller, source, and route status. +- [x] Prioritize first frontend-critical API v1 account routes used by the Chatwoot web app. +- [ ] Extend parity table with auth scope, request params, response serializer, and handler implementation status. - [ ] Patch route names and wildcard params where Gin constraints require different internal names, while preserving external URLs. - [ ] Add regression tests for route groups that previously conflicted. @@ -125,13 +141,43 @@ Tracking table: | ID | Task | Artifact | Status | | --- | --- | --- | --- | -| P2.1 | Generate Chatwoot route dump from `reference/chatwoot`. | `docs/parity/chatwoot_routes.*` | Todo | -| P2.2 | Generate GoChat route dump with `cmd/dump_routes`. | `docs/parity/gochat_routes.*` | Todo | -| P2.3 | Produce route parity table: method, path, controller/handler, auth, request params, serializer, status. | `docs/parity/route_parity.md` | Todo | -| P2.4 | Mark frontend-critical gaps from Chatwoot web app route usage. | `docs/parity/route_parity.md` | Todo | -| P2.5 | Convert existing placeholders/stubs into tracked feature tasks instead of hidden debt. | this doc and gap report | Todo | +| P2.1 | Generate Chatwoot route dump from `reference/chatwoot`. | `docs/parity/chatwoot_routes_static.md` | Review | +| P2.2 | Generate GoChat route dump with `cmd/dump_routes`. | `docs/parity/gochat_routes.txt` | Done | +| P2.3 | Produce route parity table: method, path, controller/handler, auth, request params, serializer, status. | `docs/parity/route_parity.md` | Review | +| P2.4 | Mark frontend-critical gaps from Chatwoot web app route usage. | `docs/parity/route_parity.md` | Review | +| P2.5 | Convert existing placeholders/stubs into tracked feature tasks instead of hidden debt. | this doc and gap report | Doing | | P2.6 | Add route boot regression tests for Gin wildcard/param conflicts. | router and handler tests | Todo | +Current Phase 2 route findings: + +| Type | Count | Required action | +| --- | --- | --- | +| Exact tracked critical routes | 76 | Keep covered while expanding audit scope. | +| Method-compatible update routes | 2 | Register both Rails-compatible update methods or verify frontend only calls the implemented method. | +| Missing tracked critical routes | 3 | Implement exact Chatwoot external paths before frontend reuse. | + +Missing tracked critical routes: + +| Method | Path | Chatwoot controller | Required next task | +| --- | --- | --- | --- | +| GET | `/api/v1/accounts/:account_id/contacts/:contact_id/conversations` | `api/v1/accounts/contacts/conversations#index` | Add contact conversation relation endpoint and serializer parity test. | +| POST | `/api/v1/accounts/:account_id/contacts/export` | `api/v1/accounts/contacts#export` | Add POST export route; current GoChat has `GET /contacts/export`, which is not frontend-compatible. | +| POST | `/api/v1/accounts/:account_id/conversations/:conversation_id/toggle_priority` | `api/v1/accounts/conversations#toggle_priority` | Add Chatwoot action path; current GoChat has `PATCH /conversations/:conversation_id/priority`. | + +Method-compatible routes requiring exact Rails method coverage: + +| Expected | Existing GoChat route | Required next task | +| --- | --- | --- | +| `PUT /api/v1/accounts/:account_id/conversations/:conversation_id` | `PATCH /api/v1/accounts/:account_id/conversations/:conversation_id` | Add PUT alias or prove frontend only calls PATCH. | +| `PUT /api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id` | `PATCH /api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id` | Add PUT alias or prove frontend only calls PATCH. | + +Phase 2 commands: + +```bash +env GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go run ./cmd/dump_routes > docs/parity/gochat_routes.txt +env GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go run ./cmd/route_parity +``` + ## Phase 3: Data And Serializer Parity Status: planned. @@ -314,3 +360,4 @@ env GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go run ./cmd/d - 2026-06-04: Baseline stabilized and committed as `42cdab8 chore: stabilize chatwoot parity baseline`; `go test ./...` passed and route dump reported `TOTAL: 704`. - 2026-06-04: Phase 1 search foundation added: Meilisearch config/env defaults, `SearchEngine` contract, Meilisearch HTTP wrapper with bootstrap/settings, DB fallback adapter, document builders, reindex command, and no-live-Meilisearch tests. Verified `go test ./...` in unsandboxed mode because miniredis/httptest need local sockets; route dump still reports `TOTAL: 704`. - 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`. diff --git a/docs/parity/chatwoot_routes_static.md b/docs/parity/chatwoot_routes_static.md new file mode 100644 index 00000000..ec890945 --- /dev/null +++ b/docs/parity/chatwoot_routes_static.md @@ -0,0 +1,426 @@ +# Chatwoot Route Source Declarations + +Source: `reference/chatwoot/config/routes.rb` + +Ruby is unavailable in the current workspace, so this file records static route DSL declarations with source lines. It is a repeatable fallback until `bin/rails routes` can run. + +| Line | Declaration | +| --- | --- | +| 3 | `mount_devise_token_auth_for 'User', at: 'auth', controllers: {` | +| 11 | `post 'resend_confirmation', to: 'auth/resend_confirmations#create'` | +| 19 | `get '/app', to: 'dashboard#index'` | +| 20 | `get '/app/*params', to: 'dashboard#index'` | +| 21 | `get '/app/accounts/:account_id/settings/inboxes/new/twitter', to: 'dashboard#index', as: 'app_new_twitter_inbox'` | +| 22 | `get '/app/accounts/:account_id/settings/inboxes/new/microsoft', to: 'dashboard#index', as: 'app_new_microsoft_inbox'` | +| 23 | `get '/app/accounts/:account_id/settings/inboxes/new/instagram', to: 'dashboard#index', as: 'app_new_instagram_inbox'` | +| 24 | `get '/app/accounts/:account_id/settings/inboxes/new/tiktok', to: 'dashboard#index', as: 'app_new_tiktok_inbox'` | +| 25 | `get '/app/accounts/:account_id/settings/inboxes/new/:inbox_id/agents', to: 'dashboard#index', as: 'app_twitter_inbox_agents'` | +| 26 | `get '/app/accounts/:account_id/settings/inboxes/new/:inbox_id/agents', to: 'dashboard#index', as: 'app_email_inbox_agents'` | +| 27 | `get '/app/accounts/:account_id/settings/inboxes/new/:inbox_id/agents', to: 'dashboard#index', as: 'app_instagram_inbox_agents'` | +| 28 | `get '/app/accounts/:account_id/settings/inboxes/new/:inbox_id/agents', to: 'dashboard#index', as: 'app_tiktok_inbox_agents'` | +| 29 | `get '/app/accounts/:account_id/settings/inboxes/:inbox_id', to: 'dashboard#index', as: 'app_instagram_inbox_settings'` | +| 30 | `get '/app/accounts/:account_id/settings/inboxes/:inbox_id', to: 'dashboard#index', as: 'app_tiktok_inbox_settings'` | +| 31 | `get '/app/accounts/:account_id/settings/inboxes/:inbox_id', to: 'dashboard#index', as: 'app_email_inbox_settings'` | +| 32 | `get '/app/accounts/:account_id/onboarding/inbox-setup', to: 'dashboard#index', as: 'app_onboarding_inbox_setup'` | +| 34 | `resource :widget, only: [:show]` | +| 35 | `namespace :survey do` | +| 36 | `resources :responses, only: [:show]` | +| 38 | `resource :slack_uploads, only: [:show]` | +| 41 | `get '/health', to: 'health#show'` | +| 42 | `get '/api', to: 'api#index'` | +| 43 | `namespace :api, defaults: { format: 'json' } do` | +| 44 | `namespace :v1 do` | +| 47 | `resources :accounts, only: [:create, :show, :update] do` | +| 49 | `post :update_active_at` | +| 50 | `get :cache_keys` | +| 53 | `scope module: :accounts do` | +| 54 | `namespace :actions do` | +| 55 | `resource :contact_merge, only: [:create]` | +| 57 | `resource :bulk_actions, only: [:create]` | +| 58 | `resource :onboarding, only: [:update]` | +| 59 | `resources :agents, only: [:index, :create, :update, :destroy] do` | +| 60 | `post :bulk_create, on: :collection` | +| 62 | `namespace :captain do` | +| 63 | `resource :preferences, only: [:show, :update]` | +| 64 | `resources :assistants do` | +| 66 | `post :playground` | +| 69 | `get :tools` | +| 71 | `resources :inboxes, only: [:index, :create, :destroy], param: :inbox_id` | +| 72 | `resources :scenarios` | +| 74 | `resources :assistant_responses` | +| 75 | `resources :bulk_actions, only: [:create]` | +| 76 | `resources :copilot_threads, only: [:index, :create] do` | +| 77 | `resources :copilot_messages, only: [:index, :create]` | +| 79 | `resources :custom_tools do` | +| 80 | `post :test, on: :collection` | +| 82 | `resources :documents, only: [:index, :show, :create, :destroy] do` | +| 83 | `post :sync, on: :member` | +| 85 | `resource :tasks, only: [], controller: 'tasks' do` | +| 86 | `post :rewrite` | +| 87 | `post :summarize` | +| 88 | `post :reply_suggestion` | +| 89 | `post :label_suggestion` | +| 90 | `post :follow_up` | +| 93 | `resource :saml_settings, only: [:show, :create, :update, :destroy]` | +| 94 | `resources :agent_bots, only: [:index, :create, :show, :update, :destroy] do` | +| 95 | `delete :avatar, on: :member` | +| 96 | `post :reset_access_token, on: :member` | +| 97 | `post :reset_secret, on: :member` | +| 99 | `resources :contact_inboxes, only: [] do` | +| 101 | `post :filter` | +| 104 | `resources :assignable_agents, only: [:index]` | +| 105 | `resource :audit_logs, only: [:show]` | +| 106 | `resources :callbacks, only: [] do` | +| 108 | `post :register_facebook_page` | +| 109 | `get :register_facebook_page` | +| 110 | `post :facebook_pages` | +| 111 | `post :reauthorize_page` | +| 114 | `resources :canned_responses, only: [:index, :create, :update, :destroy]` | +| 115 | `resources :automation_rules, only: [:index, :create, :show, :update, :destroy] do` | +| 116 | `post :clone` | +| 118 | `resources :macros, only: [:index, :create, :show, :update, :destroy] do` | +| 119 | `post :execute, on: :member` | +| 121 | `resources :sla_policies, only: [:index, :create, :show, :update, :destroy]` | +| 122 | `resources :custom_roles, only: [:index, :create, :show, :update, :destroy]` | +| 123 | `resources :agent_capacity_policies, only: [:index, :create, :show, :update, :destroy] do` | +| 124 | `scope module: :agent_capacity_policies do` | +| 125 | `resources :users, only: [:index, :create, :destroy]` | +| 126 | `resources :inbox_limits, only: [:create, :update, :destroy]` | +| 129 | `resources :campaigns, only: [:index, :create, :show, :update, :destroy]` | +| 130 | `resources :dashboard_apps, only: [:index, :show, :create, :update, :destroy]` | +| 131 | `namespace :channels do` | +| 132 | `resource :twilio_channel, only: [:create]` | +| 134 | `resources :conversations, only: [:index, :create, :show, :update, :destroy] do` | +| 136 | `get :meta` | +| 137 | `get :search` | +| 138 | `get :unread_counts, to: 'conversations/unread_counts#index'` | +| 139 | `post :filter` | +| 141 | `scope module: :conversations do` | +| 142 | `resources :messages, only: [:index, :create, :destroy, :update] do` | +| 144 | `post :translate` | +| 145 | `post :retry` | +| 148 | `resources :assignments, only: [:create]` | +| 149 | `resources :labels, only: [:create, :index]` | +| 150 | `resource :participants, only: [:show, :create, :update, :destroy]` | +| 151 | `resource :direct_uploads, only: [:create]` | +| 152 | `resource :draft_messages, only: [:show, :update, :destroy]` | +| 155 | `post :mute` | +| 156 | `post :unmute` | +| 157 | `post :transcript` | +| 158 | `post :toggle_status` | +| 159 | `post :toggle_priority` | +| 160 | `post :toggle_typing_status` | +| 161 | `post :update_last_seen` | +| 162 | `post :unread` | +| 163 | `post :custom_attributes` | +| 164 | `get :attachments` | +| 165 | `get :inbox_assistant` | +| 166 | `get :reporting_events if ChatwootApp.enterprise?` | +| 170 | `resources :search, only: [:index] do` | +| 172 | `get :conversations` | +| 173 | `get :messages` | +| 174 | `get :contacts` | +| 175 | `get :articles` | +| 179 | `resources :companies, only: [:index, :show, :create, :update, :destroy] do` | +| 181 | `get :search` | +| 184 | `post :destroy_custom_attributes` | +| 185 | `delete :avatar` | +| 187 | `scope module: :companies do` | +| 188 | `resources :contacts, only: [:index, :create, :destroy] do` | +| 190 | `get :search` | +| 193 | `resources :conversations, only: [:index]` | +| 194 | `resources :notes, only: [:index]` | +| 197 | `resources :contacts, only: [:index, :show, :update, :create, :destroy] do` | +| 199 | `get :active` | +| 200 | `get :search` | +| 201 | `post :filter` | +| 202 | `post :import` | +| 203 | `post :export` | +| 206 | `get :contactable_inboxes` | +| 207 | `post :destroy_custom_attributes` | +| 208 | `delete :avatar` | +| 210 | `scope module: :contacts do` | +| 211 | `resources :conversations, only: [:index]` | +| 212 | `resources :contact_inboxes, only: [:create]` | +| 213 | `resources :labels, only: [:create, :index]` | +| 214 | `resources :notes` | +| 215 | `get :attachments, to: 'attachments#index'` | +| 216 | `post :call, on: :member, to: 'calls#create' if ChatwootApp.enterprise?` | +| 219 | `resources :csat_survey_responses, only: [:index] do` | +| 221 | `get :metrics` | +| 222 | `get :download` | +| 225 | `patch :update if ChatwootApp.enterprise?` | +| 228 | `resources :applied_slas, only: [:index] do` | +| 230 | `get :metrics` | +| 231 | `get :download` | +| 234 | `resources :reporting_events, only: [:index] if ChatwootApp.enterprise?` | +| 237 | `resources :whatsapp_calls, only: [:show] do` | +| 239 | `post :accept` | +| 240 | `post :reject` | +| 241 | `post :terminate` | +| 242 | `post :upload_recording` | +| 245 | `post :initiate` | +| 250 | `resources :custom_attribute_definitions, only: [:index, :show, :create, :update, :destroy]` | +| 251 | `resources :custom_filters, only: [:index, :show, :create, :update, :destroy]` | +| 252 | `resources :inboxes, only: [:index, :show, :create, :update, :destroy] do` | +| 253 | `get :assignable_agents, on: :member` | +| 254 | `get :campaigns, on: :member` | +| 255 | `get :agent_bot, on: :member` | +| 256 | `post :set_agent_bot, on: :member` | +| 257 | `delete :avatar, on: :member` | +| 258 | `post :sync_templates, on: :member` | +| 259 | `get :health, on: :member` | +| 260 | `post :register_webhook, on: :member` | +| 261 | `post :reset_secret, on: :member` | +| 263 | `resource :conference, only: %i[create destroy], controller: 'conference' do` | +| 264 | `get :token, on: :member` | +| 266 | `post :enable_whatsapp_calling, on: :member` | +| 267 | `post :disable_whatsapp_calling, on: :member` | +| 270 | `resource :csat_template, only: [:show, :create], controller: 'inbox_csat_templates' do` | +| 271 | `post :analyze, on: :collection` | +| 275 | `resources :inbox_members, only: [:create, :show], param: :inbox_id do` | +| 277 | `delete :destroy` | +| 278 | `patch :update` | +| 281 | `resources :labels, only: [:index, :show, :create, :update, :destroy]` | +| 283 | `resources :notifications, only: [:index, :update, :destroy] do` | +| 285 | `post :read_all` | +| 286 | `get :unread_count` | +| 287 | `post :destroy_all` | +| 290 | `post :snooze` | +| 291 | `post :unread` | +| 294 | `resource :notification_settings, only: [:show, :update]` | +| 296 | `resources :teams do` | +| 297 | `resources :team_members, only: [:index, :create] do` | +| 299 | `delete :destroy` | +| 300 | `patch :update` | +| 306 | `resources :assignment_policies do` | +| 307 | `resources :inboxes, only: [:index, :create, :destroy], module: :assignment_policies` | +| 310 | `resources :inboxes, only: [] do` | +| 311 | `resource :assignment_policy, only: [:show, :create, :destroy], module: :inboxes` | +| 314 | `namespace :twitter do` | +| 315 | `resource :authorization, only: [:create]` | +| 318 | `namespace :microsoft do` | +| 319 | `resource :authorization, only: [:create]` | +| 322 | `namespace :google do` | +| 323 | `resource :authorization, only: [:create]` | +| 326 | `namespace :instagram do` | +| 327 | `resource :authorization, only: [:create]` | +| 330 | `namespace :tiktok do` | +| 331 | `resource :authorization, only: [:create]` | +| 334 | `namespace :notion do` | +| 335 | `resource :authorization, only: [:create]` | +| 338 | `namespace :whatsapp do` | +| 339 | `resource :authorization, only: [:create]` | +| 342 | `resources :webhooks, only: [:index, :create, :update, :destroy]` | +| 343 | `namespace :integrations do` | +| 344 | `resources :apps, only: [:index, :show]` | +| 345 | `resources :hooks, only: [:show, :create, :update, :destroy] do` | +| 347 | `post :process_event` | +| 350 | `resource :slack, only: [:create, :update, :destroy], controller: 'slack' do` | +| 352 | `get :list_all_channels` | +| 355 | `resource :dyte, controller: 'dyte', only: [] do` | +| 357 | `post :create_a_meeting` | +| 358 | `post :add_participant_to_meeting` | +| 361 | `resource :shopify, controller: 'shopify', only: [:destroy] do` | +| 363 | `post :auth` | +| 364 | `get :orders` | +| 367 | `resource :linear, controller: 'linear', only: [] do` | +| 369 | `delete :destroy` | +| 370 | `get :teams` | +| 371 | `get :team_entities` | +| 372 | `post :create_issue` | +| 373 | `post :link_issue` | +| 374 | `post :unlink_issue` | +| 375 | `get :search_issue` | +| 376 | `get :linked_issues` | +| 379 | `resource :notion, controller: 'notion', only: [] do` | +| 381 | `delete :destroy` | +| 385 | `resources :portals do` | +| 387 | `patch :archive` | +| 388 | `delete :logo` | +| 389 | `post :send_instructions` | +| 390 | `get :ssl_status` | +| 392 | `resources :categories do` | +| 393 | `post :reorder, on: :collection` | +| 395 | `namespace :articles do` | +| 396 | `resource :bulk_actions, only: [] do` | +| 397 | `post :translate` | +| 398 | `patch :update_status` | +| 399 | `patch :update_category` | +| 400 | `delete :delete_articles` | +| 403 | `resources :articles do` | +| 404 | `post :reorder, on: :collection` | +| 408 | `resources :upload, only: [:create]` | +| 414 | `namespace :integrations do` | +| 415 | `resources :webhooks, only: [:create]` | +| 419 | `post 'auth/saml_login', to: 'auth#saml_login'` | +| 421 | `resource :profile, only: [:show, :update] do` | +| 422 | `delete :avatar, on: :collection` | +| 424 | `post :availability` | +| 425 | `post :auto_offline` | +| 426 | `put :set_active_account` | +| 427 | `post :resend_confirmation` | +| 428 | `post :reset_access_token` | +| 432 | `scope module: 'profile' do` | +| 433 | `resource :mfa, controller: 'mfa', only: [:show, :create, :destroy] do` | +| 434 | `post :verify` | +| 435 | `post :backup_codes` | +| 440 | `resource :notification_subscriptions, only: [:create, :destroy]` | +| 442 | `namespace :widget do` | +| 443 | `resource :direct_uploads, only: [:create]` | +| 444 | `resource :config, only: [:create]` | +| 445 | `resources :campaigns, only: [:index]` | +| 446 | `resources :events, only: [:create]` | +| 447 | `resources :messages, only: [:index, :create, :update]` | +| 448 | `resources :conversations, only: [:index, :create] do` | +| 450 | `post :destroy_custom_attributes` | +| 451 | `post :set_custom_attributes` | +| 452 | `post :update_last_seen` | +| 453 | `post :toggle_typing` | +| 454 | `post :transcript` | +| 455 | `get :toggle_status` | +| 458 | `resource :contact, only: [:show, :update] do` | +| 460 | `post :destroy_custom_attributes` | +| 461 | `patch :set_user` | +| 464 | `resources :inbox_members, only: [:index]` | +| 465 | `resources :labels, only: [:create, :destroy]` | +| 466 | `namespace :integrations do` | +| 467 | `resource :dyte, controller: 'dyte', only: [] do` | +| 469 | `post :add_participant_to_meeting` | +| 476 | `namespace :v2 do` | +| 477 | `resources :accounts, only: [:create] do` | +| 478 | `scope module: :accounts do` | +| 479 | `resources :summary_reports, only: [] do` | +| 481 | `get :agent` | +| 482 | `get :team` | +| 483 | `get :inbox` | +| 484 | `get :label` | +| 485 | `get :channel` | +| 488 | `resources :reports, only: [:index] do` | +| 490 | `get :summary` | +| 491 | `get :bot_summary` | +| 492 | `get :agents` | +| 493 | `get :inboxes` | +| 494 | `get :labels` | +| 495 | `get :teams` | +| 496 | `get :conversations` | +| 497 | `get :conversations_summary` | +| 498 | `get :conversation_traffic` | +| 499 | `get :bot_metrics` | +| 500 | `get :inbox_label_matrix` | +| 501 | `get :first_response_time_distribution` | +| 502 | `get :outgoing_messages_count` | +| 505 | `resource :year_in_review, only: [:show]` | +| 506 | `resources :live_reports, only: [] do` | +| 508 | `get :conversation_metrics` | +| 509 | `get :grouped_conversation_metrics` | +| 518 | `namespace :enterprise, defaults: { format: 'json' } do` | +| 519 | `namespace :api do` | +| 520 | `namespace :v1 do` | +| 521 | `resources :accounts do` | +| 523 | `post :checkout` | +| 524 | `post :subscription` | +| 525 | `get :limits` | +| 526 | `post :toggle_deletion` | +| 527 | `post :topup_checkout` | +| 533 | `post 'webhooks/stripe', to: 'webhooks/stripe#process_payload'` | +| 534 | `post 'webhooks/firecrawl', to: 'webhooks/firecrawl#process_payload'` | +| 540 | `namespace :platform, defaults: { format: 'json' } do` | +| 541 | `namespace :api do` | +| 542 | `namespace :v1 do` | +| 543 | `resources :users, only: [:create, :show, :update, :destroy] do` | +| 545 | `get :login` | +| 546 | `post :token` | +| 549 | `resources :agent_bots, only: [:index, :create, :show, :update, :destroy] do` | +| 550 | `delete :avatar, on: :member` | +| 552 | `resources :accounts, only: [:index, :create, :show, :update, :destroy] do` | +| 553 | `resources :account_users, only: [:index, :create] do` | +| 555 | `delete :destroy` | +| 558 | `resources :email_channel_migrations, only: [:create]` | +| 566 | `namespace :public, defaults: { format: 'json' } do` | +| 567 | `namespace :api do` | +| 568 | `namespace :v1 do` | +| 569 | `resources :inboxes do` | +| 570 | `scope module: :inboxes do` | +| 571 | `resources :contacts, only: [:create, :show, :update] do` | +| 572 | `resources :conversations, only: [:index, :create, :show] do` | +| 574 | `post :toggle_status` | +| 575 | `post :toggle_typing` | +| 576 | `post :update_last_seen` | +| 579 | `resources :messages, only: [:index, :create, :update]` | +| 585 | `resources :csat_survey, only: [:show, :update]` | +| 590 | `get 'hc/:slug', to: 'public/api/v1/portals#show'` | +| 591 | `get 'hc/:slug/sitemap.xml', to: 'public/api/v1/portals#sitemap'` | +| 592 | `get 'hc/:slug/:locale', to: 'public/api/v1/portals#show', as: :public_portal_locale` | +| 593 | `get 'hc/:slug/:locale/search', to: 'public/api/v1/portals/search#index', as: :portal_search` | +| 594 | `get 'hc/:slug/:locale/articles', to: 'public/api/v1/portals/articles#index'` | +| 595 | `get 'hc/:slug/:locale/categories', to: 'public/api/v1/portals/categories#index'` | +| 596 | `get 'hc/:slug/:locale/categories/:category_slug', to: 'public/api/v1/portals/categories#show', as: :public_portal_category` | +| 597 | `get 'hc/:slug/:locale/categories/:category_slug/articles', to: 'public/api/v1/portals/articles#index'` | +| 598 | `get 'hc/:slug/articles/:article_slug.png', to: 'public/api/v1/portals/articles#tracking_pixel'` | +| 599 | `get 'hc/:slug/articles/:article_slug.md', to: 'public/api/v1/portals/articles#show_markdown', as: :public_portal_article_markdown,` | +| 601 | `get 'hc/:slug/articles/:article_slug', to: 'public/api/v1/portals/articles#show', as: :public_portal_article` | +| 605 | `resource :app, only: [:index] do` | +| 606 | `resources :accounts do` | +| 607 | `resources :conversations, only: [:show]` | +| 613 | `mount Facebook::Messenger::Server, at: 'bot'` | +| 614 | `get 'webhooks/twitter', to: 'api/v1/webhooks#twitter_crc'` | +| 615 | `post 'webhooks/twitter', to: 'api/v1/webhooks#twitter_events'` | +| 616 | `post 'webhooks/line/:line_channel_id', to: 'webhooks/line#process_payload'` | +| 617 | `post 'webhooks/telegram/:bot_token', to: 'webhooks/telegram#process_payload'` | +| 618 | `post 'webhooks/sms/:phone_number', to: 'webhooks/sms#process_payload'` | +| 619 | `get 'webhooks/whatsapp/:phone_number', to: 'webhooks/whatsapp#verify'` | +| 620 | `post 'webhooks/whatsapp/:phone_number', to: 'webhooks/whatsapp#process_payload'` | +| 621 | `get 'webhooks/instagram', to: 'webhooks/instagram#verify'` | +| 622 | `post 'webhooks/instagram', to: 'webhooks/instagram#events'` | +| 623 | `post 'webhooks/tiktok', to: 'webhooks/tiktok#events'` | +| 624 | `post 'webhooks/shopify', to: 'webhooks/shopify#events'` | +| 626 | `namespace :twitter do` | +| 627 | `resource :callback, only: [:show]` | +| 630 | `namespace :linear do` | +| 631 | `resource :callback, only: [:show]` | +| 634 | `namespace :shopify do` | +| 635 | `resource :callback, only: [:show]` | +| 638 | `namespace :twilio do` | +| 639 | `resources :callback, only: [:create]` | +| 640 | `resources :delivery_status, only: [:create]` | +| 643 | `post 'voice/call/:phone', to: 'voice#call_twiml', as: :voice_call` | +| 644 | `post 'voice/status/:phone', to: 'voice#status', as: :voice_status` | +| 645 | `post 'voice/conference_status/:phone', to: 'voice#conference_status', as: :voice_conference_status` | +| 646 | `post 'voice/recording_status/:phone', to: 'voice#recording_status', as: :voice_recording_status` | +| 650 | `get 'microsoft/callback', to: 'microsoft/callbacks#show'` | +| 651 | `get 'google/callback', to: 'google/callbacks#show'` | +| 652 | `get 'instagram/callback', to: 'instagram/callbacks#show'` | +| 653 | `get 'tiktok/callback', to: 'tiktok/callbacks#show'` | +| 654 | `get 'notion/callback', to: 'notion/callbacks#show'` | +| 657 | `get '.well-known/assetlinks.json' => 'android_app#assetlinks'` | +| 658 | `get '.well-known/apple-app-site-association' => 'apple_app#site_association'` | +| 659 | `get '.well-known/microsoft-identity-association.json' => 'microsoft#identity_association'` | +| 660 | `get '.well-known/cf-custom-hostname-challenge/:id', to: 'custom_domains#verify'` | +| 669 | `get 'super_admin/logout', to: 'super_admin/devise/sessions#destroy'` | +| 670 | `namespace :super_admin do` | +| 673 | `resource :app_config, only: [:show, :create]` | +| 674 | `resource :push_diagnostics, only: [:show, :create] do` | +| 675 | `post :destroy_subscriptions, on: :collection` | +| 679 | `resources :accounts, only: [:index, :new, :create, :show, :edit, :update, :destroy] do` | +| 680 | `post :seed, on: :member` | +| 681 | `post :reset_cache, on: :member` | +| 683 | `resources :users, only: [:index, :new, :create, :show, :edit, :update, :destroy] do` | +| 684 | `delete :avatar, on: :member, action: :destroy_avatar` | +| 687 | `resources :access_tokens, only: [:index, :show]` | +| 688 | `resources :installation_configs, only: [:index, :new, :create, :show, :edit, :update]` | +| 689 | `resources :agent_bots, only: [:index, :new, :create, :show, :edit, :update, :destroy] do` | +| 690 | `delete :avatar, on: :member, action: :destroy_avatar` | +| 692 | `resources :platform_apps, only: [:index, :new, :create, :show, :edit, :update, :destroy]` | +| 693 | `resources :platform_banners` | +| 694 | `resource :instance_status, only: [:show]` | +| 696 | `resource :settings, only: [:show] do` | +| 697 | `get :refresh, on: :collection` | +| 701 | `resources :account_users, only: [:new, :create, :show, :destroy]` | +| 704 | `mount Sidekiq::Web => '/monitoring/sidekiq'` | +| 708 | `namespace :installation do` | +| 709 | `get 'onboarding', to: 'onboarding#index'` | +| 710 | `post 'onboarding', to: 'onboarding#create'` | +| 715 | `get '/swagger/*path', to: 'swagger#respond'` | +| 716 | `get '/swagger', to: 'swagger#respond'` | +| 720 | `resources :widget_tests, only: [:index] unless Rails.env.production?` | diff --git a/docs/parity/gochat_routes.txt b/docs/parity/gochat_routes.txt new file mode 100644 index 00000000..2df7102a --- /dev/null +++ b/docs/parity/gochat_routes.txt @@ -0,0 +1,705 @@ +CONNECT /webhooks/:channel_type/:identifier +DELETE /api/v1/accounts/:account_id +DELETE /api/v1/accounts/:account_id/agent_bot_inboxes/:agent_bot_inbox_id +DELETE /api/v1/accounts/:account_id/agent_bots/:agent_bot_id +DELETE /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/avatar +DELETE /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/bot_rules/:rule_id +DELETE /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/trigger_configs/:trigger_config_id +DELETE /api/v1/accounts/:account_id/agent_capacity_policies/:id +DELETE /api/v1/accounts/:account_id/agents/:agent_id +DELETE /api/v1/accounts/:account_id/assignment_policies/:policy_id +DELETE /api/v1/accounts/:account_id/assignment_policies/:policy_id/inboxes/:inbox_id +DELETE /api/v1/accounts/:account_id/assignment_policies_v2/:id +DELETE /api/v1/accounts/:account_id/assignment_policies_v2/:id/inboxes/:inbox_id +DELETE /api/v1/accounts/:account_id/assignment_policy/:policy_id +DELETE /api/v1/accounts/:account_id/automation_rules/:automation_id +DELETE /api/v1/accounts/:account_id/campaigns/:campaign_id +DELETE /api/v1/accounts/:account_id/canned_responses/:id +DELETE /api/v1/accounts/:account_id/captain/assistant_responses/:response_id +DELETE /api/v1/accounts/:account_id/captain/assistants/:assistant_id +DELETE /api/v1/accounts/:account_id/captain/assistants/:assistant_id/documents/:document_id +DELETE /api/v1/accounts/:account_id/captain/assistants/:assistant_id/inboxes/:inbox_id +DELETE /api/v1/accounts/:account_id/captain/assistants/:assistant_id/scenarios/:scenario_id +DELETE /api/v1/accounts/:account_id/captain/copilot_threads/:thread_id +DELETE /api/v1/accounts/:account_id/captain/custom_tools/:tool_id +DELETE /api/v1/accounts/:account_id/captain/documents/:document_id +DELETE /api/v1/accounts/:account_id/captain/preferences/ +DELETE /api/v1/accounts/:account_id/captain/scenarios/:scenario_id +DELETE /api/v1/accounts/:account_id/channels/facebook_channel/:fb_id +DELETE /api/v1/accounts/:account_id/companies/:company_id +DELETE /api/v1/accounts/:account_id/companies/:company_id/contacts/:contact_id +DELETE /api/v1/accounts/:account_id/companies/:company_id/notes/:note_id +DELETE /api/v1/accounts/:account_id/contacts/:contact_id +DELETE /api/v1/accounts/:account_id/contacts/:contact_id/contact_inboxes/:inbox_id +DELETE /api/v1/accounts/:account_id/contacts/:contact_id/custom_attributes +DELETE /api/v1/accounts/:account_id/contacts/:contact_id/custom_attributes/:attribute_name +DELETE /api/v1/accounts/:account_id/contacts/:contact_id/notes/:note_id +DELETE /api/v1/accounts/:account_id/conversations/:conversation_id +DELETE /api/v1/accounts/:account_id/conversations/:conversation_id/custom_attributes/:attribute_name +DELETE /api/v1/accounts/:account_id/conversations/:conversation_id/draft_messages/:draft_id +DELETE /api/v1/accounts/:account_id/conversations/:conversation_id/labels/:tag_id +DELETE /api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id +DELETE /api/v1/accounts/:account_id/conversations/:conversation_id/participants/:user_id +DELETE /api/v1/accounts/:account_id/conversations/:conversation_id/whatsapp_calls/:call_id +DELETE /api/v1/accounts/:account_id/custom_attribute_definitions/:id +DELETE /api/v1/accounts/:account_id/custom_filters/:id +DELETE /api/v1/accounts/:account_id/custom_roles/:id +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/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/ +DELETE /api/v1/accounts/:account_id/inboxes/:inbox_id/avatar +DELETE /api/v1/accounts/:account_id/inboxes/:inbox_id/email_channels/:em_id +DELETE /api/v1/accounts/:account_id/inboxes/:inbox_id/inbox_limits/:id +DELETE /api/v1/accounts/:account_id/inboxes/:inbox_id/instagram_channels/:ig_id +DELETE /api/v1/accounts/:account_id/inboxes/:inbox_id/instagram_comments/:comment_id +DELETE /api/v1/accounts/:account_id/inboxes/:inbox_id/line_channels/:line_id +DELETE /api/v1/accounts/:account_id/inboxes/:inbox_id/members/:user_id +DELETE /api/v1/accounts/:account_id/inboxes/:inbox_id/tiktok_channels/:tt_id +DELETE /api/v1/accounts/:account_id/inboxes/:inbox_id/twilio_sms_channels/:tw_id +DELETE /api/v1/accounts/:account_id/inboxes/:inbox_id/web_widget +DELETE /api/v1/accounts/:account_id/inboxes/:inbox_id/web_widget/pre_chat_form +DELETE /api/v1/accounts/:account_id/inboxes/:inbox_id/web_widget/theme_config +DELETE /api/v1/accounts/:account_id/integrations/hooks/:id +DELETE /api/v1/accounts/:account_id/integrations/linear/ +DELETE /api/v1/accounts/:account_id/integrations/notion/ +DELETE /api/v1/accounts/:account_id/integrations/shopify/ +DELETE /api/v1/accounts/:account_id/integrations/slack/ +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/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 +DELETE /api/v1/accounts/:account_id/portals/:portal_id/articles/:article_id +DELETE /api/v1/accounts/:account_id/portals/:portal_id/categories/:category_id +DELETE /api/v1/accounts/:account_id/portals/:portal_id/folders/:folder_id +DELETE /api/v1/accounts/:account_id/portals/:portal_id/logo +DELETE /api/v1/accounts/:account_id/portals/:portal_id/members/:member_id +DELETE /api/v1/accounts/:account_id/saml_settings/:id +DELETE /api/v1/accounts/:account_id/sla_policies/:id +DELETE /api/v1/accounts/:account_id/sla_policies/:id/inboxes/:inbox_id +DELETE /api/v1/accounts/:account_id/tags/:tag_id +DELETE /api/v1/accounts/:account_id/teams/:team_id +DELETE /api/v1/accounts/:account_id/teams/:team_id/team_members/ +DELETE /api/v1/accounts/:account_id/twitter_channels/:twitter_id +DELETE /api/v1/accounts/:account_id/users/:user_id +DELETE /api/v1/accounts/:account_id/webhooks/:webhook_id +DELETE /api/v1/auth/logout +DELETE /api/v1/notification_subscriptions/:identifier +DELETE /api/v1/notifications/:id +DELETE /api/v1/notifications/destroy_all +DELETE /api/v1/profile/avatar +DELETE /api/v1/profile/mfa/ +DELETE /api/v1/push_subscriptions/:id +DELETE /platform/api/v1/accounts/:account_id +DELETE /platform/api/v1/accounts/:account_id/account_users/:user_id +DELETE /platform/api/v1/agent_bots/:id +DELETE /platform/api/v1/apps/:id +DELETE /platform/api/v1/apps/:id/permissibles/:permissible_id +DELETE /platform/api/v1/banners/:id +DELETE /platform/api/v1/installation_configs/:id +DELETE /platform/api/v1/users/:id +DELETE /webhooks/:channel_type/:identifier +GET /api/v1/accounts/ +GET /api/v1/accounts/:account_id +GET /api/v1/accounts/:account_id/agent_bot_inboxes/ +GET /api/v1/accounts/:account_id/agent_bot_inboxes/by_bot +GET /api/v1/accounts/:account_id/agent_bots/ +GET /api/v1/accounts/:account_id/agent_bots/:agent_bot_id +GET /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/bot_rules/ +GET /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/bot_rules/:rule_id +GET /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/trigger_configs/ +GET /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/trigger_configs/:trigger_config_id +GET /api/v1/accounts/:account_id/agent_capacity_policies/ +GET /api/v1/accounts/:account_id/agent_capacity_policies/:id +GET /api/v1/accounts/:account_id/agents +GET /api/v1/accounts/:account_id/agents/:agent_id +GET /api/v1/accounts/:account_id/applied_slas/download +GET /api/v1/accounts/:account_id/applied_slas/metrics +GET /api/v1/accounts/:account_id/assignable_agents +GET /api/v1/accounts/:account_id/assignment_policies/ +GET /api/v1/accounts/:account_id/assignment_policies/:policy_id +GET /api/v1/accounts/:account_id/assignment_policies/:policy_id/inboxes +GET /api/v1/accounts/:account_id/assignment_policies_v2 +GET /api/v1/accounts/:account_id/assignment_policies_v2/:id +GET /api/v1/accounts/:account_id/assignment_policies_v2/:id/inboxes +GET /api/v1/accounts/:account_id/assignment_policy +GET /api/v1/accounts/:account_id/audit_logs/ +GET /api/v1/accounts/:account_id/audit_logs/:id +GET /api/v1/accounts/:account_id/automation_rules/ +GET /api/v1/accounts/:account_id/automation_rules/:automation_id +GET /api/v1/accounts/:account_id/banners +GET /api/v1/accounts/:account_id/cache_keys +GET /api/v1/accounts/:account_id/campaigns/ +GET /api/v1/accounts/:account_id/campaigns/:campaign_id +GET /api/v1/accounts/:account_id/canned_responses/ +GET /api/v1/accounts/:account_id/canned_responses/:id +GET /api/v1/accounts/:account_id/canned_responses/search +GET /api/v1/accounts/:account_id/captain/assistant_responses/ +GET /api/v1/accounts/:account_id/captain/assistant_responses/:response_id +GET /api/v1/accounts/:account_id/captain/assistants/ +GET /api/v1/accounts/:account_id/captain/assistants/:assistant_id +GET /api/v1/accounts/:account_id/captain/assistants/:assistant_id/documents/ +GET /api/v1/accounts/:account_id/captain/assistants/:assistant_id/documents/:document_id +GET /api/v1/accounts/:account_id/captain/assistants/:assistant_id/inboxes +GET /api/v1/accounts/:account_id/captain/assistants/:assistant_id/scenarios/ +GET /api/v1/accounts/:account_id/captain/assistants/:assistant_id/scenarios/:scenario_id +GET /api/v1/accounts/:account_id/captain/assistants/tools +GET /api/v1/accounts/:account_id/captain/copilot/stream +GET /api/v1/accounts/:account_id/captain/copilot_messages/ +GET /api/v1/accounts/:account_id/captain/copilot_threads/ +GET /api/v1/accounts/:account_id/captain/copilot_threads/:thread_id +GET /api/v1/accounts/:account_id/captain/copilot_threads/:thread_id/copilot_messages/ +GET /api/v1/accounts/:account_id/captain/custom_tools/ +GET /api/v1/accounts/:account_id/captain/custom_tools/:tool_id +GET /api/v1/accounts/:account_id/captain/documents/ +GET /api/v1/accounts/:account_id/captain/documents/:document_id +GET /api/v1/accounts/:account_id/captain/preferences/ +GET /api/v1/accounts/:account_id/captain/scenarios/ +GET /api/v1/accounts/:account_id/captain/scenarios/:scenario_id +GET /api/v1/accounts/:account_id/captain/tasks/follow_up +GET /api/v1/accounts/:account_id/captain/tasks/label_suggestion +GET /api/v1/accounts/:account_id/channels/facebook_channel/:fb_id +GET /api/v1/accounts/:account_id/channels/facebook_channel/authorization +GET /api/v1/accounts/:account_id/companies/ +GET /api/v1/accounts/:account_id/companies/:company_id +GET /api/v1/accounts/:account_id/companies/:company_id/contacts +GET /api/v1/accounts/:account_id/companies/:company_id/conversations +GET /api/v1/accounts/:account_id/companies/:company_id/notes +GET /api/v1/accounts/:account_id/companies/search +GET /api/v1/accounts/:account_id/contact_inboxes/filter +GET /api/v1/accounts/:account_id/contacts/ +GET /api/v1/accounts/:account_id/contacts/:contact_id +GET /api/v1/accounts/:account_id/contacts/:contact_id/contact_inboxes +GET /api/v1/accounts/:account_id/contacts/:contact_id/contactable_inboxes +GET /api/v1/accounts/:account_id/contacts/:contact_id/notes +GET /api/v1/accounts/:account_id/contacts/:contact_id/notes/:note_id +GET /api/v1/accounts/:account_id/contacts/active +GET /api/v1/accounts/:account_id/contacts/export +GET /api/v1/accounts/:account_id/contacts/search +GET /api/v1/accounts/:account_id/conversations/ +GET /api/v1/accounts/:account_id/conversations/:conversation_id +GET /api/v1/accounts/:account_id/conversations/:conversation_id/attachments +GET /api/v1/accounts/:account_id/conversations/:conversation_id/draft_messages/ +GET /api/v1/accounts/:account_id/conversations/:conversation_id/draft_messages/:draft_id +GET /api/v1/accounts/:account_id/conversations/:conversation_id/labels +GET /api/v1/accounts/:account_id/conversations/:conversation_id/messages/ +GET /api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id +GET /api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id/delivery_status/ +GET /api/v1/accounts/:account_id/conversations/:conversation_id/participants/ +GET /api/v1/accounts/:account_id/conversations/:conversation_id/whatsapp_calls/ +GET /api/v1/accounts/:account_id/conversations/:conversation_id/whatsapp_calls/:call_id +GET /api/v1/accounts/:account_id/conversations/meta +GET /api/v1/accounts/:account_id/conversations/search +GET /api/v1/accounts/:account_id/conversations/unread_counts +GET /api/v1/accounts/:account_id/csat_survey_responses/ +GET /api/v1/accounts/:account_id/csat_survey_responses/download +GET /api/v1/accounts/:account_id/csat_survey_responses/metrics +GET /api/v1/accounts/:account_id/custom_attribute_definitions/ +GET /api/v1/accounts/:account_id/custom_attribute_definitions/:id +GET /api/v1/accounts/:account_id/custom_filters/ +GET /api/v1/accounts/:account_id/custom_filters/:id +GET /api/v1/accounts/:account_id/custom_roles/ +GET /api/v1/accounts/:account_id/custom_roles/:id +GET /api/v1/accounts/:account_id/dashboard_apps +GET /api/v1/accounts/:account_id/dashboard_apps/:dashboard_app_id +GET /api/v1/accounts/:account_id/dashboard_apps/:dashboard_app_id/widgets +GET /api/v1/accounts/:account_id/dashboard_apps/search +GET /api/v1/accounts/:account_id/draft_messages/count +GET /api/v1/accounts/:account_id/draft_messages/search +GET /api/v1/accounts/:account_id/email_channel_migrations/ +GET /api/v1/accounts/:account_id/email_channels/ +GET /api/v1/accounts/:account_id/google/callback +GET /api/v1/accounts/:account_id/google/oauth +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/inboxes/ +GET /api/v1/accounts/:account_id/inboxes/:inbox_id +GET /api/v1/accounts/:account_id/inboxes/:inbox_id/agent_bot +GET /api/v1/accounts/:account_id/inboxes/:inbox_id/assignable_agents +GET /api/v1/accounts/:account_id/inboxes/:inbox_id/assignment_policy/ +GET /api/v1/accounts/:account_id/inboxes/:inbox_id/assignment_policy_v2/ +GET /api/v1/accounts/:account_id/inboxes/:inbox_id/campaigns +GET /api/v1/accounts/:account_id/inboxes/:inbox_id/csat_template +GET /api/v1/accounts/:account_id/inboxes/:inbox_id/email_channels/:em_id +GET /api/v1/accounts/:account_id/inboxes/:inbox_id/health +GET /api/v1/accounts/:account_id/inboxes/:inbox_id/instagram_channels/:ig_id +GET /api/v1/accounts/:account_id/inboxes/:inbox_id/instagram_comments/:comment_id/replies +GET /api/v1/accounts/:account_id/inboxes/:inbox_id/instagram_comments/media/:media_id +GET /api/v1/accounts/:account_id/inboxes/:inbox_id/line_channels/:line_id +GET /api/v1/accounts/:account_id/inboxes/:inbox_id/members +GET /api/v1/accounts/:account_id/inboxes/:inbox_id/out_of_office +GET /api/v1/accounts/:account_id/inboxes/:inbox_id/tiktok_channels/:tt_id +GET /api/v1/accounts/:account_id/inboxes/:inbox_id/twilio_sms_channels/:tw_id +GET /api/v1/accounts/:account_id/inboxes/:inbox_id/web_widget/offline_messages +GET /api/v1/accounts/:account_id/inboxes/:inbox_id/web_widget/pre_chat_form +GET /api/v1/accounts/:account_id/inboxes/:inbox_id/web_widget/theme_config +GET /api/v1/accounts/:account_id/inboxes/:inbox_id/web_widget_config +GET /api/v1/accounts/:account_id/inboxes/:inbox_id/working_hours +GET /api/v1/accounts/:account_id/instagram/callback +GET /api/v1/accounts/:account_id/instagram/oauth +GET /api/v1/accounts/:account_id/instagram/webhooks +GET /api/v1/accounts/:account_id/instagram_channels/ +GET /api/v1/accounts/:account_id/integrations/apps/ +GET /api/v1/accounts/:account_id/integrations/apps/:id +GET /api/v1/accounts/:account_id/integrations/hooks/ +GET /api/v1/accounts/:account_id/integrations/hooks/:id +GET /api/v1/accounts/:account_id/integrations/linear/linked_issues +GET /api/v1/accounts/:account_id/integrations/linear/search_issue +GET /api/v1/accounts/:account_id/integrations/linear/team_entities +GET /api/v1/accounts/:account_id/integrations/linear/teams +GET /api/v1/accounts/:account_id/integrations/shopify/orders +GET /api/v1/accounts/:account_id/integrations/slack/list_all_channels +GET /api/v1/accounts/:account_id/labels/ +GET /api/v1/accounts/:account_id/labels/:tag_id +GET /api/v1/accounts/:account_id/labels/:tag_id/conversations +GET /api/v1/accounts/:account_id/line_channels/ +GET /api/v1/accounts/:account_id/live_reports/conversation_metrics +GET /api/v1/accounts/:account_id/live_reports/grouped_conversation_metrics +GET /api/v1/accounts/:account_id/macros/ +GET /api/v1/accounts/:account_id/macros/:macro_id +GET /api/v1/accounts/:account_id/microsoft/callback +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/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 +GET /api/v1/accounts/:account_id/platform_apps/:platform_app_id/permissibles +GET /api/v1/accounts/:account_id/platform_apps/search +GET /api/v1/accounts/:account_id/portals/ +GET /api/v1/accounts/:account_id/portals/:portal_id +GET /api/v1/accounts/:account_id/portals/:portal_id/articles/ +GET /api/v1/accounts/:account_id/portals/:portal_id/articles/:article_id +GET /api/v1/accounts/:account_id/portals/:portal_id/articles/:article_id/edit +GET /api/v1/accounts/:account_id/portals/:portal_id/articles/search +GET /api/v1/accounts/:account_id/portals/:portal_id/articles/status_counts +GET /api/v1/accounts/:account_id/portals/:portal_id/categories/ +GET /api/v1/accounts/:account_id/portals/:portal_id/categories/:category_id +GET /api/v1/accounts/:account_id/portals/:portal_id/categories/:category_id/articles +GET /api/v1/accounts/:account_id/portals/:portal_id/folders/ +GET /api/v1/accounts/:account_id/portals/:portal_id/folders/:folder_id +GET /api/v1/accounts/:account_id/portals/:portal_id/members/ +GET /api/v1/accounts/:account_id/portals/:portal_id/members/:member_id +GET /api/v1/accounts/:account_id/portals/:portal_id/ssl_status +GET /api/v1/accounts/:account_id/reporting_events +GET /api/v1/accounts/:account_id/reports/agents +GET /api/v1/accounts/:account_id/reports/bot_metrics +GET /api/v1/accounts/:account_id/reports/bot_summary +GET /api/v1/accounts/:account_id/reports/conversation_traffic +GET /api/v1/accounts/:account_id/reports/conversations +GET /api/v1/accounts/:account_id/reports/conversations_summary +GET /api/v1/accounts/:account_id/reports/first_response_time_distribution +GET /api/v1/accounts/:account_id/reports/inbox_label_matrix +GET /api/v1/accounts/:account_id/reports/inboxes +GET /api/v1/accounts/:account_id/reports/labels +GET /api/v1/accounts/:account_id/reports/outgoing_messages_count +GET /api/v1/accounts/:account_id/reports/summary +GET /api/v1/accounts/:account_id/reports/teams +GET /api/v1/accounts/:account_id/saml_settings/:id +GET /api/v1/accounts/:account_id/search +GET /api/v1/accounts/:account_id/search/articles +GET /api/v1/accounts/:account_id/search/contacts +GET /api/v1/accounts/:account_id/search/conversations +GET /api/v1/accounts/:account_id/search/messages +GET /api/v1/accounts/:account_id/sla_policies +GET /api/v1/accounts/:account_id/sla_policies/:id +GET /api/v1/accounts/:account_id/sla_policies/:id/inboxes +GET /api/v1/accounts/:account_id/summary_reports/agent +GET /api/v1/accounts/:account_id/summary_reports/channel +GET /api/v1/accounts/:account_id/summary_reports/inbox +GET /api/v1/accounts/:account_id/summary_reports/label +GET /api/v1/accounts/:account_id/summary_reports/team +GET /api/v1/accounts/:account_id/tags/ +GET /api/v1/accounts/:account_id/tags/:tag_id +GET /api/v1/accounts/:account_id/tags/:tag_id/conversations +GET /api/v1/accounts/:account_id/teams/ +GET /api/v1/accounts/:account_id/teams/:team_id +GET /api/v1/accounts/:account_id/teams/:team_id/team_members/ +GET /api/v1/accounts/:account_id/tiktok_channels/ +GET /api/v1/accounts/:account_id/twilio_sms_channels/ +GET /api/v1/accounts/:account_id/twitter/callback +GET /api/v1/accounts/:account_id/twitter/oauth +GET /api/v1/accounts/:account_id/twitter/webhooks +GET /api/v1/accounts/:account_id/twitter_channels/authorization +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/all +GET /api/v1/auth/confirm_email +GET /api/v1/auth/mfa/status +GET /api/v1/auth/oauth/authorize +GET /api/v1/ldap/config +GET /api/v1/notifications +GET /api/v1/notifications/unread_count +GET /api/v1/oidc/authorize +GET /api/v1/oidc/callback +GET /api/v1/oidc/config +GET /api/v1/oidc/discovery +GET /api/v1/profile +GET /api/v1/profile/mfa/ +GET /api/v1/push_subscriptions +GET /api/v1/saml/login +GET /api/v1/saml/metadata +GET /api/v1/saml/slo +GET /api/v1/sso_sessions/:session_id +GET /api/v1/sso_sessions/user/:user_id +GET /api/v1/sso_sessions/user/:user_id/count +GET /cable +GET /health +GET /platform/api/v1/accounts +GET /platform/api/v1/accounts/:account_id +GET /platform/api/v1/accounts/:account_id/account_users +GET /platform/api/v1/agent_bots +GET /platform/api/v1/agent_bots/:id +GET /platform/api/v1/apps +GET /platform/api/v1/apps/:id +GET /platform/api/v1/apps/:id/access_tokens +GET /platform/api/v1/apps/:id/permissibles +GET /platform/api/v1/apps/search +GET /platform/api/v1/banners +GET /platform/api/v1/banners/:id +GET /platform/api/v1/installation_configs +GET /platform/api/v1/installation_configs/:id +GET /platform/api/v1/users +GET /platform/api/v1/users/:id +GET /platform/api/v1/users/:id/login +GET /platform/api/v1/users/:id/token +GET /platform/api/v1/widget_tests/ +GET /platform/api/v1/widget_tests/:type +GET /public/api/v1/conversations/:conversation_uuid/csats +GET /webhooks/:channel_type/:identifier +GET /widget/cable_token +GET /widget/conversations +GET /widget/conversations/:id/messages +GET /widget/widget/:website_token/pre_chat_form +GET /widget/widget/:website_token/theme_config +GET /widget/widget/:website_token/uploads/:upload_uuid +GET /ws +HEAD /webhooks/:channel_type/:identifier +OPTIONS /webhooks/:channel_type/:identifier +PATCH /api/v1/accounts/:account_id/agent_bot_inboxes/:agent_bot_inbox_id/status +PATCH /api/v1/accounts/:account_id/channels/facebook_channel/:fb_id +PATCH /api/v1/accounts/:account_id/contacts/:contact_id/notes/:note_id +PATCH /api/v1/accounts/:account_id/conversations/:conversation_id +PATCH /api/v1/accounts/:account_id/conversations/:conversation_id/draft_messages/:draft_id +PATCH /api/v1/accounts/:account_id/conversations/:conversation_id/labels +PATCH /api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id +PATCH /api/v1/accounts/:account_id/conversations/:conversation_id/participants/ +PATCH /api/v1/accounts/:account_id/conversations/:conversation_id/participants/:user_id +PATCH /api/v1/accounts/:account_id/conversations/:conversation_id/priority +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/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 +PATCH /api/v1/accounts/:account_id/inboxes/:inbox_id/line_channels/:line_id +PATCH /api/v1/accounts/:account_id/inboxes/:inbox_id/members/:user_id +PATCH /api/v1/accounts/:account_id/inboxes/:inbox_id/members/update_multiple +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/teams/:team_id/team_members/ +PATCH /platform/api/v1/accounts/:account_id +PATCH /platform/api/v1/users/:id +PATCH /webhooks/:channel_type/:identifier +PATCH /widget/contact +POST /api/v1/accounts/ +POST /api/v1/accounts/:account_id/agent_bot_inboxes/ +POST /api/v1/accounts/:account_id/agent_bots/ +POST /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/bot_rules/ +POST /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/delete_avatar +POST /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/reset_access_token +POST /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/reset_secret +POST /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/reset_token +POST /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/trigger_configs/ +POST /api/v1/accounts/:account_id/agent_capacity_policies/ +POST /api/v1/accounts/:account_id/agents +POST /api/v1/accounts/:account_id/agents/bulk_assign +POST /api/v1/accounts/:account_id/agents/bulk_create +POST /api/v1/accounts/:account_id/agents/bulk_unassign +POST /api/v1/accounts/:account_id/assignment_policies/ +POST /api/v1/accounts/:account_id/assignment_policies/:policy_id/inboxes +POST /api/v1/accounts/:account_id/assignment_policies_v2 +POST /api/v1/accounts/:account_id/assignment_policies_v2/:id/inboxes +POST /api/v1/accounts/:account_id/assignment_policy +POST /api/v1/accounts/:account_id/automation_rules/ +POST /api/v1/accounts/:account_id/automation_rules/:automation_id/clone +POST /api/v1/accounts/:account_id/automation_rules/:automation_id/toggle_active +POST /api/v1/accounts/:account_id/bulk_actions +POST /api/v1/accounts/:account_id/campaigns/ +POST /api/v1/accounts/:account_id/campaigns/:campaign_id/start +POST /api/v1/accounts/:account_id/campaigns/:campaign_id/stop +POST /api/v1/accounts/:account_id/canned_responses/ +POST /api/v1/accounts/:account_id/captain/assistant_responses/ +POST /api/v1/accounts/:account_id/captain/assistant_responses/process +POST /api/v1/accounts/:account_id/captain/assistants/ +POST /api/v1/accounts/:account_id/captain/assistants/:assistant_id/documents/ +POST /api/v1/accounts/:account_id/captain/assistants/:assistant_id/inboxes +POST /api/v1/accounts/:account_id/captain/assistants/:assistant_id/playground +POST /api/v1/accounts/:account_id/captain/assistants/:assistant_id/scenarios/ +POST /api/v1/accounts/:account_id/captain/bulk_actions/ +POST /api/v1/accounts/:account_id/captain/conversation_insights/:conversation_id/analyze_participants +POST /api/v1/accounts/:account_id/captain/conversation_insights/:conversation_id/extract_action_items +POST /api/v1/accounts/:account_id/captain/conversation_insights/:conversation_id/suggest_labels +POST /api/v1/accounts/:account_id/captain/copilot/suggest_replies +POST /api/v1/accounts/:account_id/captain/copilot/summarize +POST /api/v1/accounts/:account_id/captain/copilot/translate +POST /api/v1/accounts/:account_id/captain/copilot_messages/ +POST /api/v1/accounts/:account_id/captain/copilot_threads/ +POST /api/v1/accounts/:account_id/captain/copilot_threads/:thread_id/copilot_messages/ +POST /api/v1/accounts/:account_id/captain/copilot_threads/:thread_id/messages +POST /api/v1/accounts/:account_id/captain/custom_tools/ +POST /api/v1/accounts/:account_id/captain/custom_tools/test +POST /api/v1/accounts/:account_id/captain/documents/ +POST /api/v1/accounts/:account_id/captain/documents/:document_id/sync +POST /api/v1/accounts/:account_id/captain/preferences/ +POST /api/v1/accounts/:account_id/captain/scenarios/ +POST /api/v1/accounts/:account_id/captain/tasks/reply_suggestion +POST /api/v1/accounts/:account_id/captain/tasks/reply_suggestion/stream +POST /api/v1/accounts/:account_id/captain/tasks/rewrite +POST /api/v1/accounts/:account_id/captain/tasks/rewrite/stream +POST /api/v1/accounts/:account_id/captain/tasks/summarize +POST /api/v1/accounts/:account_id/captain/tasks/summarize/stream +POST /api/v1/accounts/:account_id/channels/facebook_channel/ +POST /api/v1/accounts/:account_id/channels/facebook_channel/oauth_callback +POST /api/v1/accounts/:account_id/channels/facebook_channel/reauthorize +POST /api/v1/accounts/:account_id/companies/ +POST /api/v1/accounts/:account_id/companies/:company_id/contacts/:contact_id +POST /api/v1/accounts/:account_id/companies/:company_id/notes +POST /api/v1/accounts/:account_id/contact_merge +POST /api/v1/accounts/:account_id/contacts/ +POST /api/v1/accounts/:account_id/contacts/:contact_id/contact_inboxes +POST /api/v1/accounts/:account_id/contacts/:contact_id/custom_attributes/ +POST /api/v1/accounts/:account_id/contacts/:contact_id/destroy_custom_attributes +POST /api/v1/accounts/:account_id/contacts/:contact_id/notes +POST /api/v1/accounts/:account_id/contacts/filter +POST /api/v1/accounts/:account_id/contacts/import +POST /api/v1/accounts/:account_id/contacts/merge +POST /api/v1/accounts/:account_id/conversations/ +POST /api/v1/accounts/:account_id/conversations/:conversation_id/assign +POST /api/v1/accounts/:account_id/conversations/:conversation_id/assignments +POST /api/v1/accounts/:account_id/conversations/:conversation_id/custom_attributes +POST /api/v1/accounts/:account_id/conversations/:conversation_id/custom_attributes/ +POST /api/v1/accounts/:account_id/conversations/:conversation_id/draft_messages/ +POST /api/v1/accounts/:account_id/conversations/:conversation_id/labels +POST /api/v1/accounts/:account_id/conversations/:conversation_id/messages/ +POST /api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id/retry +POST /api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id/translate +POST /api/v1/accounts/:account_id/conversations/:conversation_id/mute +POST /api/v1/accounts/:account_id/conversations/:conversation_id/participants/ +POST /api/v1/accounts/:account_id/conversations/:conversation_id/toggle_status +POST /api/v1/accounts/:account_id/conversations/:conversation_id/toggle_typing_status +POST /api/v1/accounts/:account_id/conversations/:conversation_id/transcript +POST /api/v1/accounts/:account_id/conversations/:conversation_id/unmute +POST /api/v1/accounts/:account_id/conversations/:conversation_id/unread +POST /api/v1/accounts/:account_id/conversations/:conversation_id/update_last_seen +POST /api/v1/accounts/:account_id/conversations/:conversation_id/whatsapp_calls/ +POST /api/v1/accounts/:account_id/conversations/filter +POST /api/v1/accounts/:account_id/csat_survey_responses/:id/update_review_notes +POST /api/v1/accounts/:account_id/custom_attribute_definitions/ +POST /api/v1/accounts/:account_id/custom_filters/ +POST /api/v1/accounts/:account_id/custom_roles/ +POST /api/v1/accounts/:account_id/dashboard_apps +POST /api/v1/accounts/:account_id/dashboard_apps/:dashboard_app_id/widgets +POST /api/v1/accounts/:account_id/direct_uploads +POST /api/v1/accounts/:account_id/email_channel_migrations/ +POST /api/v1/accounts/:account_id/email_channels/ +POST /api/v1/accounts/:account_id/google/webhooks +POST /api/v1/accounts/:account_id/google_channels/oauth_callback +POST /api/v1/accounts/:account_id/hooks/ +POST /api/v1/accounts/:account_id/hooks/:id/process_event +POST /api/v1/accounts/:account_id/inbox_members/ +POST /api/v1/accounts/:account_id/inboxes/ +POST /api/v1/accounts/:account_id/inboxes/:inbox_id/assignment_policy/ +POST /api/v1/accounts/:account_id/inboxes/:inbox_id/assignment_policy_v2/ +POST /api/v1/accounts/:account_id/inboxes/:inbox_id/csat_template +POST /api/v1/accounts/:account_id/inboxes/:inbox_id/csat_template/analyze +POST /api/v1/accounts/:account_id/inboxes/:inbox_id/inbox_limits +POST /api/v1/accounts/:account_id/inboxes/:inbox_id/instagram_comments/:comment_id/hide +POST /api/v1/accounts/:account_id/inboxes/:inbox_id/instagram_comments/:comment_id/reply +POST /api/v1/accounts/:account_id/inboxes/:inbox_id/members +POST /api/v1/accounts/:account_id/inboxes/:inbox_id/register_webhook +POST /api/v1/accounts/:account_id/inboxes/:inbox_id/reset_secret +POST /api/v1/accounts/:account_id/inboxes/:inbox_id/set_agent_bot +POST /api/v1/accounts/:account_id/inboxes/:inbox_id/sync_templates +POST /api/v1/accounts/:account_id/inboxes/web_widget +POST /api/v1/accounts/:account_id/instagram/webhooks +POST /api/v1/accounts/:account_id/instagram_channels/ +POST /api/v1/accounts/:account_id/integrations/hooks/ +POST /api/v1/accounts/:account_id/integrations/hooks/:id/process_event +POST /api/v1/accounts/:account_id/integrations/linear/create_issue +POST /api/v1/accounts/:account_id/integrations/linear/link_issue +POST /api/v1/accounts/:account_id/integrations/linear/unlink_issue +POST /api/v1/accounts/:account_id/integrations/shopify/auth +POST /api/v1/accounts/:account_id/integrations/slack/ +POST /api/v1/accounts/:account_id/labels/ +POST /api/v1/accounts/:account_id/labels/batch_add +POST /api/v1/accounts/:account_id/labels/batch_remove +POST /api/v1/accounts/:account_id/line_channels/ +POST /api/v1/accounts/:account_id/macros/ +POST /api/v1/accounts/:account_id/macros/:macro_id/clone +POST /api/v1/accounts/:account_id/macros/:macro_id/execute +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/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 +POST /api/v1/accounts/:account_id/portals/ +POST /api/v1/accounts/:account_id/portals/:portal_id/archive +POST /api/v1/accounts/:account_id/portals/:portal_id/articles/ +POST /api/v1/accounts/:account_id/portals/:portal_id/articles/bulk_actions +POST /api/v1/accounts/:account_id/portals/:portal_id/articles/bulk_delete +POST /api/v1/accounts/:account_id/portals/:portal_id/articles/bulk_update_status +POST /api/v1/accounts/:account_id/portals/:portal_id/articles/reorder +POST /api/v1/accounts/:account_id/portals/:portal_id/categories/ +POST /api/v1/accounts/:account_id/portals/:portal_id/categories/reorder +POST /api/v1/accounts/:account_id/portals/:portal_id/folders/ +POST /api/v1/accounts/:account_id/portals/:portal_id/members/ +POST /api/v1/accounts/:account_id/portals/:portal_id/send_instructions +POST /api/v1/accounts/:account_id/saml_settings/ +POST /api/v1/accounts/:account_id/saml_settings/:id/toggle_active +POST /api/v1/accounts/:account_id/sla_policies +POST /api/v1/accounts/:account_id/sla_policies/:id/inboxes +POST /api/v1/accounts/:account_id/tags/ +POST /api/v1/accounts/:account_id/teams/ +POST /api/v1/accounts/:account_id/teams/:team_id/team_members/ +POST /api/v1/accounts/:account_id/tiktok_channels/ +POST /api/v1/accounts/:account_id/twilio_sms_channels/ +POST /api/v1/accounts/:account_id/twitter/webhooks +POST /api/v1/accounts/:account_id/twitter_channels/oauth_callback +POST /api/v1/accounts/:account_id/update_active_at +POST /api/v1/accounts/:account_id/upload +POST /api/v1/accounts/:account_id/users +POST /api/v1/accounts/:account_id/webhooks +POST /api/v1/auth/login +POST /api/v1/auth/login/mfa +POST /api/v1/auth/mfa/backup_codes +POST /api/v1/auth/mfa/disable +POST /api/v1/auth/mfa/enable +POST /api/v1/auth/mfa/verify +POST /api/v1/auth/oauth/callback +POST /api/v1/auth/refresh +POST /api/v1/auth/register +POST /api/v1/auth/reset_password +POST /api/v1/auth/switch_account +POST /api/v1/ldap/login +POST /api/v1/ldap/test +POST /api/v1/notification_subscriptions +POST /api/v1/notifications/:id/snooze +POST /api/v1/notifications/:id/unread +POST /api/v1/notifications/read_all +POST /api/v1/profile/auto_offline +POST /api/v1/profile/availability +POST /api/v1/profile/mfa/ +POST /api/v1/profile/mfa/backup_codes +POST /api/v1/profile/mfa/verify +POST /api/v1/profile/resend_confirmation +POST /api/v1/profile/reset_access_token +POST /api/v1/push_subscriptions +POST /api/v1/saml/acs +POST /api/v1/saml/slo +POST /api/v1/sso_sessions/:session_id/terminate +POST /api/v1/sso_sessions/user/:user_id/terminate_all +POST /platform/api/v1/accounts +POST /platform/api/v1/accounts/:account_id/account_users +POST /platform/api/v1/agent_bots +POST /platform/api/v1/agent_bots/:id/delete_avatar +POST /platform/api/v1/agent_bots/:id/reset +POST /platform/api/v1/agent_bots/:id/reset_secret +POST /platform/api/v1/agent_bots/:id/reset_token +POST /platform/api/v1/apps +POST /platform/api/v1/apps/:id/permissibles +POST /platform/api/v1/apps/:id/regenerate_access_token +POST /platform/api/v1/banners +POST /platform/api/v1/installation_configs +POST /platform/api/v1/users +POST /platform/api/v1/users/:id/login +POST /platform/api/v1/users/:id/token +POST /public/api/v1/conversations/:conversation_uuid/csats +POST /webhooks/:channel_type/:identifier +POST /widget/conversations/:id/toggle_typing +POST /widget/direct_uploads +POST /widget/init +POST /widget/messages +POST /widget/widget/:website_token/offline_message +POST /widget/widget/:website_token/uploads +PUT /api/v1/accounts/:account_id +PUT /api/v1/accounts/:account_id/agent_bots/:agent_bot_id +PUT /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/bot_rules/:rule_id +PUT /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/trigger_configs/:trigger_config_id +PUT /api/v1/accounts/:account_id/agent_capacity_policies/:id +PUT /api/v1/accounts/:account_id/agents/:agent_id +PUT /api/v1/accounts/:account_id/assignment_policies/:policy_id +PUT /api/v1/accounts/:account_id/assignment_policies_v2/:id +PUT /api/v1/accounts/:account_id/assignment_policy/:policy_id +PUT /api/v1/accounts/:account_id/automation_rules/:automation_id +PUT /api/v1/accounts/:account_id/campaigns/:campaign_id +PUT /api/v1/accounts/:account_id/canned_responses/:id +PUT /api/v1/accounts/:account_id/captain/assistant_responses/:response_id +PUT /api/v1/accounts/:account_id/captain/assistants/:assistant_id +PUT /api/v1/accounts/:account_id/captain/assistants/:assistant_id/scenarios/:scenario_id +PUT /api/v1/accounts/:account_id/captain/custom_tools/:tool_id +PUT /api/v1/accounts/:account_id/captain/preferences/ +PUT /api/v1/accounts/:account_id/captain/scenarios/:scenario_id +PUT /api/v1/accounts/:account_id/companies/:company_id +PUT /api/v1/accounts/:account_id/contacts/:contact_id +PUT /api/v1/accounts/:account_id/conversations/:conversation_id/whatsapp_calls/:call_id +PUT /api/v1/accounts/:account_id/custom_attribute_definitions/:id +PUT /api/v1/accounts/:account_id/custom_filters/:id +PUT /api/v1/accounts/:account_id/custom_roles/:id +PUT /api/v1/accounts/:account_id/dashboard_apps/:dashboard_app_id +PUT /api/v1/accounts/:account_id/dashboard_apps/:dashboard_app_id/widgets/:widget_index +PUT /api/v1/accounts/:account_id/hooks/:id +PUT /api/v1/accounts/:account_id/inboxes/:inbox_id +PUT /api/v1/accounts/:account_id/inboxes/:inbox_id/assignment_policy/:policy_id +PUT /api/v1/accounts/:account_id/inboxes/:inbox_id/web_widget/pre_chat_form +PUT /api/v1/accounts/:account_id/inboxes/:inbox_id/web_widget/theme_config +PUT /api/v1/accounts/:account_id/inboxes/:inbox_id/web_widget_config +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/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 +PUT /api/v1/accounts/:account_id/portals/:portal_id/categories/:category_id +PUT /api/v1/accounts/:account_id/portals/:portal_id/folders/:folder_id +PUT /api/v1/accounts/:account_id/portals/:portal_id/members/:member_id +PUT /api/v1/accounts/:account_id/saml_settings/:id +PUT /api/v1/accounts/:account_id/settings +PUT /api/v1/accounts/:account_id/sla_policies/:id +PUT /api/v1/accounts/:account_id/tags/:tag_id +PUT /api/v1/accounts/:account_id/teams/:team_id +PUT /api/v1/accounts/:account_id/web_widgets/offline_messages/:offline_message_id/convert +PUT /api/v1/accounts/:account_id/web_widgets/offline_messages/:offline_message_id/dismiss +PUT /api/v1/accounts/:account_id/webhooks/:webhook_id +PUT /api/v1/ldap/config +PUT /api/v1/notifications/:id +PUT /api/v1/oidc/config +PUT /api/v1/profile +PUT /api/v1/profile/avatar +PUT /api/v1/profile/set_active_account +PUT /platform/api/v1/agent_bots/:id +PUT /platform/api/v1/agent_bots/:id/avatar +PUT /platform/api/v1/apps/:id +PUT /platform/api/v1/banners/:id +PUT /platform/api/v1/installation_configs/:id +PUT /webhooks/:channel_type/:identifier +TRACE /webhooks/:channel_type/:identifier +TOTAL: 704 diff --git a/docs/parity/route_parity.md b/docs/parity/route_parity.md new file mode 100644 index 00000000..fa7bfad0 --- /dev/null +++ b/docs/parity/route_parity.md @@ -0,0 +1,116 @@ +# Route Parity Report + +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`. + +Summary: 76 exact, 2 method-compatible, 0 parameter-compatible, 3 missing out of 81 tracked critical routes. + +## Missing Critical Routes + +| Method | Chatwoot Path | GoChat Match | Controller | Source | Status | +| --- | --- | --- | --- | --- | --- | +| GET | `/api/v1/accounts/:account_id/contacts/:contact_id/conversations` | `-` | `api/v1/accounts/contacts/conversations#index` | `routes.rb:211` | missing | +| POST | `/api/v1/accounts/:account_id/contacts/export` | `-` | `api/v1/accounts/contacts#export` | `routes.rb:204` | missing | +| POST | `/api/v1/accounts/:account_id/conversations/:conversation_id/toggle_priority` | `-` | `api/v1/accounts/conversations#toggle_priority` | `routes.rb:159` | missing | + +## Method-Compatible Routes + +These routes exist at the same path but with a compatible HTTP method. Rails resource `update` routes expose both `PATCH` and `PUT`; GoChat currently exposes only one method for some update endpoints. Keep these tracked until both methods are registered or the frontend is proven to call only the implemented method. + +| Method | Chatwoot Path | GoChat Match | Controller | Source | Status | +| --- | --- | --- | --- | --- | --- | +| PUT | `/api/v1/accounts/:account_id/conversations/:conversation_id` | `PATCH /api/v1/accounts/:account_id/conversations/:conversation_id` | `api/v1/accounts/conversations#update` | `routes.rb:134` | method-compatible | +| PUT | `/api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id` | `PATCH /api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id` | `api/v1/accounts/conversations/messages#update` | `routes.rb:142` | method-compatible | + +## Parameter-Compatible Routes + +These routes exist with equivalent method and path shape but different parameter names. They need handler/serializer parity review, and exact external path compatibility must be preserved. + +| Method | Chatwoot Path | GoChat Match | Controller | Source | Status | +| --- | --- | --- | --- | --- | --- | +| - | - | - | - | - | none | + +## Exact Critical Routes + +| Method | Chatwoot Path | GoChat Match | Controller | Source | Status | +| --- | --- | --- | --- | --- | --- | +| 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/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/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/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/sla_policies/:id` | `/api/v1/accounts/:account_id/sla_policies/:id` | `api/v1/accounts/sla_policies#destroy` | `routes.rb:121` | 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/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/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/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 | +| GET | `/api/v1/accounts/:account_id/contacts/:contact_id/notes` | `/api/v1/accounts/:account_id/contacts/:contact_id/notes` | `api/v1/accounts/contacts/notes#index` | `routes.rb:214` | exact | +| GET | `/api/v1/accounts/:account_id/contacts/search` | `/api/v1/accounts/:account_id/contacts/search` | `api/v1/accounts/contacts#search` | `routes.rb:201` | exact | +| GET | `/api/v1/accounts/:account_id/conversations/` | `/api/v1/accounts/:account_id/conversations/` | `api/v1/accounts/conversations#index` | `routes.rb:134` | exact | +| GET | `/api/v1/accounts/:account_id/conversations/:conversation_id` | `/api/v1/accounts/:account_id/conversations/:conversation_id` | `api/v1/accounts/conversations#show` | `routes.rb:134` | exact | +| GET | `/api/v1/accounts/:account_id/conversations/:conversation_id/labels` | `/api/v1/accounts/:account_id/conversations/:conversation_id/labels` | `api/v1/accounts/conversations/labels#index` | `routes.rb:149` | exact | +| GET | `/api/v1/accounts/:account_id/conversations/:conversation_id/messages/` | `/api/v1/accounts/:account_id/conversations/:conversation_id/messages/` | `api/v1/accounts/conversations/messages#index` | `routes.rb:142` | exact | +| GET | `/api/v1/accounts/:account_id/conversations/meta` | `/api/v1/accounts/:account_id/conversations/meta` | `api/v1/accounts/conversations#meta` | `routes.rb:136` | exact | +| GET | `/api/v1/accounts/:account_id/conversations/search` | `/api/v1/accounts/:account_id/conversations/search` | `api/v1/accounts/conversations#search` | `routes.rb:137` | exact | +| 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_roles/` | `/api/v1/accounts/:account_id/custom_roles/` | `api/v1/accounts/custom_roles#index` | `routes.rb:122` | 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/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/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/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 | +| GET | `/api/v1/accounts/:account_id/search/conversations` | `/api/v1/accounts/:account_id/search/conversations` | `api/v1/accounts/search#conversations` | `routes.rb:173` | exact | +| 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 | +| POST | `/api/v1/accounts/` | `/api/v1/accounts/` | `api/v1/accounts#create` | `routes.rb:47` | 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/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 | +| POST | `/api/v1/accounts/:account_id/contacts/:contact_id/notes` | `/api/v1/accounts/:account_id/contacts/:contact_id/notes` | `api/v1/accounts/contacts/notes#create` | `routes.rb:214` | exact | +| POST | `/api/v1/accounts/:account_id/contacts/filter` | `/api/v1/accounts/:account_id/contacts/filter` | `api/v1/accounts/contacts#filter` | `routes.rb:202` | exact | +| POST | `/api/v1/accounts/:account_id/contacts/import` | `/api/v1/accounts/:account_id/contacts/import` | `api/v1/accounts/contacts#import` | `routes.rb:203` | exact | +| POST | `/api/v1/accounts/:account_id/conversations/` | `/api/v1/accounts/:account_id/conversations/` | `api/v1/accounts/conversations#create` | `routes.rb:134` | exact | +| POST | `/api/v1/accounts/:account_id/conversations/:conversation_id/assignments` | `/api/v1/accounts/:account_id/conversations/:conversation_id/assignments` | `api/v1/accounts/conversations/assignments#create` | `routes.rb:148` | exact | +| POST | `/api/v1/accounts/:account_id/conversations/:conversation_id/labels` | `/api/v1/accounts/:account_id/conversations/:conversation_id/labels` | `api/v1/accounts/conversations/labels#create` | `routes.rb:149` | exact | +| POST | `/api/v1/accounts/:account_id/conversations/:conversation_id/messages/` | `/api/v1/accounts/:account_id/conversations/:conversation_id/messages/` | `api/v1/accounts/conversations/messages#create` | `routes.rb:142` | exact | +| POST | `/api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id/retry` | `/api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id/retry` | `api/v1/accounts/conversations/messages#retry` | `routes.rb:145` | exact | +| POST | `/api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id/translate` | `/api/v1/accounts/:account_id/conversations/:conversation_id/messages/:message_id/translate` | `api/v1/accounts/conversations/messages#translate` | `routes.rb:144` | exact | +| 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/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/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/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/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/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/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/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/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/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/sla_policies/:id` | `/api/v1/accounts/:account_id/sla_policies/:id` | `api/v1/accounts/sla_policies#update` | `routes.rb:121` | exact |