ClientId is a product-level GUID registered at developer.connectwise.com,
not per-MSP. Moved to settings.CW_CLIENT_ID env var. MSPs now only
provide site URL, company ID, public key, and private key.
Also added newline handling note to post_note() — CW Developer Guide
states \n is unsupported in JSON bodies. Needs sandbox testing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three new session-scoped endpoints for posting session documentation
to linked PSA tickets:
- GET /sessions/{id}/psa-post/preview — generates PSA-formatted content,
fetches ticket details and available statuses, counts previous posts
- POST /sessions/{id}/psa-post — posts note to CW ticket with optional
status update, logs all actions in psa_post_log audit trail
- GET /sessions/{id}/psa-posts — returns post history for the session
New schemas: PsaPostRequest, PsaPostResponse, PsaPreviewResponse,
PsaPostLogResponse.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces NotImplementedError stubs with working implementations:
- post_note maps note_type to CW flag fields (internalAnalysisFlag,
resolutionFlag, detailDescriptionFlag) with appropriate visibility
and notification settings
- update_ticket_status uses CW JSON Patch format to update status
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Audit trail for notes posted to PSA systems. Tracks session ID,
ticket ID, note type, content, status (success/failed), and any
status changes made alongside the note post.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add PSACache class with get/set/invalidate/clear operations and TTL expiry.
Board statuses are cached for 1 hour in the provider.
Cache is cleared when a PSA connection is re-tested.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add three new endpoints under /integrations/psa:
- GET /tickets/search: search CW tickets with query, board_id, status_id filters
- GET /tickets/{ticket_id}: fetch a single ticket by ID
- GET /tickets/{ticket_id}/statuses: get available statuses for a ticket's board
Add PSATicketSearchResult and PSATicketStatusItem schemas.
All endpoints require engineer_or_admin auth.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement all remaining NotImplementedError stubs in ConnectWiseProvider:
- search_tickets: query by summary with board_id, status_id, include_closed filters
- get_ticket_statuses: fetch statuses for a service board
- list_companies: list companies with optional status filter
- get_company: fetch a single company by ID
- get_ticket_configurations: fetch configs attached to a ticket
- Extract shared _map_ticket helper to reduce duplication
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PATCH /sessions/{id}/ticket-link validates ticket exists in ConnectWise
before linking, supports unlinking by sending null, and returns ticket
details on success.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add columns to link sessions to PSA tickets and connections. Includes
migration 059, model relationship, and response schema fields.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace NotImplementedError stub with real implementation that fetches
a ticket by ID via CW REST API and maps it to PSATicket.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Five endpoints under /integrations/psa/connections:
- GET (any auth user), POST/PUT/DELETE/test (owner+ only)
- Create tests CW connection before saving; update re-tests on cred change
- Credentials decrypted only for masked hints in responses
- Three CI-safe tests: empty GET, engineer 403, delete 404
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PsaConnectionCreate, PsaConnectionUpdate, PsaConnectionResponse,
and PsaConnectionTestResponse — registered in schemas __init__.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ConnectWiseProvider implements PSAProvider with test_connection() that
calls GET /system/info to verify credentials and connectivity. All other
methods raise NotImplementedError with slice references for future work.
Provider registry (get_provider_for_account) looks up the account's
PsaConnection, decrypts stored credentials, and instantiates the
correct provider. Currently supports connectwise only.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements ConnectWiseClient in services/psa/connectwise/client.py with:
- API key auth (Base64 companyId+publicKey:privateKey) + clientId header
- Accept header pinned to CW API version 2025.16
- Dynamic base URL resolution via /login/companyinfo (cloud vs on-premise)
- SSRF prevention: validates against known CW domains, rejects private IPs
- Retry with exponential backoff for timeouts and 5xx errors
- 429 rate limit handling with Retry-After header respect
- Error mapping: 401/403/404/429/5xx to typed PSA exceptions
- Paginated GET with while-loop pattern (max 1000 per page)
- JSON Patch array format for PATCH requests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5-slice plan covering foundation (abstraction layer, encryption,
connection CRUD, frontend), ticket linking, ticket search, update
ticket modal, and member mapping. Slice 1 has full task-level detail;
slices 2-5 are outlined for iterative planning.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>