wulf-pulse/PULSE_DATABASE_SKILL.md
lorentz b98c67482a feat: QuickBooks Online integration
- Add QBO OAuth2 client with token refresh (lib/services/qbo-client.ts)
- Add QBO sync service for invoices, payments, deposits, purchases, journal entries, reports (lib/services/qbo-sync-service.ts)
- Add QBO types (lib/types/qbo.ts)
- Add API routes: /api/qbo/auth, /api/qbo/sync, /api/qbo/disconnect
- Add /admin/qbo status and sync management page
- Add legal pages: /legal/eula, /legal/privacy (Intuit app assessment)
- Add QBO nav link under Admin
- Fix reports: remove invalid summarize_column_by, add accounting_method from Preferences API, add showrows=all&showcols=all
- Add CashFlow report type alongside P&L and BalanceSheet
- Add NoReportData check to skip empty report months
- Add intuit_tid capture in error messages
- Add redirect: follow for cluster routing
- Migration 051: qbo_tokens, qbo_invoices, qbo_payments, qbo_deposits, qbo_transactions, qbo_reports tables

Also includes earlier work:
- Ping flap suppression pipeline step
- Ticket digest reports with LLM analysis
- Zabbix WAN monitor and gap analysis
- Kiosk is_deleted filter fixes
- Datto RMM ping target enrichment
- Entity sync soft-delete detection
2026-03-17 07:39:55 -04:00

18 KiB

Pulse Database Skill — Query Reference

Purpose: This document describes the PostgreSQL database behind Pulse, an MSP operations platform built by Wulf Consulting. Use it to query Autotask PSA data, RMM alerts, security agents, backup status, IT documentation, engagement metrics, and more.

Connection

  • Host: pulse-postgres (Docker) or localhost:5432
  • Database: pulse_autotask
  • User: pulse_user
  • Read-only queries only — no INSERT/UPDATE/DELETE

Data Domains at a Glance

Domain Key Tables Approx Rows Description
Autotask PSA tickets, time_entries, companies, contacts, resources, configuration_items, contracts, projects, tasks, ticket_notes 109K tickets, 154K time entries, 7K CIs Service desk, billing, contracts, clients
Datto RMM datto_rmm_alerts, datto_rmm_devices, datto_rmm_sites 4.3K alerts, 3.6K devices Remote monitoring & management
SentinelOne s1_agents, s1_threats, s1_sites 2.8K agents, 4.1K threats Endpoint security
Veeam veeam_organizations, veeam_backup_jobs, veeam_backup_agents, veeam_alarms, veeam_protected_workloads, veeam_repositories, veeam_backup_servers ~2.5K total Backup & disaster recovery
IT Glue itg_organizations, itg_configurations, itg_passwords, itg_flexible_assets, itg_contacts, itg_documents, itg_expirations, itg_domains, itg_locations 14.7K configs, 7.1K contacts IT documentation
Microsoft 365 graph_users, teams_meetings, teams_meeting_attendees, engagement_snapshots 14.3K meetings Teams meetings, activity reports
Zoom zoom_users, zoom_meetings, zoom_meeting_participants, zoom_calls 2.8K calls, meetings Zoom calls and meetings
Billing billing_items 95K items Invoice line items tied to tickets/projects/tasks

1. Autotask PSA — Core Service Desk

tickets (~109K rows, 68 columns)

The central table. Each row is a service ticket.

Key columns:

  • id (bigint PK) — Autotask ticket ID
  • title (varchar) — ticket subject line
  • description (text) — full body/description
  • status (int) — FK to statuses.value
  • priority (int) — FK to priorities.value
  • queue_id (int) — FK to queues.value
  • source (int) — how the ticket was created (see Source Codes below)
  • company_id (bigint) — FK to companies.id
  • contact_id (bigint) — FK to contacts.id
  • assigned_resource_id (bigint) — FK to resources.id
  • configuration_item_id (bigint) — FK to configuration_items.id
  • contract_id (bigint) — FK to contracts.id
  • project_id (bigint) — FK to projects.id
  • issue_type (int), sub_issue_type (int) — classification
  • ticket_type (int) — 1=Incident, 2=Service Request, 5=Alert
  • create_date (timestamp) — when opened
  • due_date_time (timestamp) — SLA due
  • completed_date (timestamp) — when closed
  • resolved_date_time (timestamp) — when resolved
  • first_response_date_time (timestamp) — first response SLA timestamp
  • last_activity_date (timestamp) — most recent update
  • monitor_id (bigint) — RMM monitor that created this ticket (if source=8)
  • monitor_type_id (int) — type of monitor
  • is_deleted (boolean) — soft delete flag

Source codes (tickets.source):

Value Meaning Count
8 Monitoring Alert (RMM/Datto) 80,736
4 Email 14,872
21 Portal 2,500
2 Phone/Voice 1,781
-1 Insourced 1,092
-2 Outsourced 1,088
35 Phish Alert 726
6 API 488
17 Internal Alert 453

Ticket types:

Value Meaning Count
1 Incident 8,409
2 Service Request 3,217
5 Alert 29,885
NULL Unclassified 67,737

companies (~4K rows, 41 columns)

Client/customer organizations.

Key columns:

  • id (bigint PK)
  • company_name (varchar) — display name
  • company_number (varchar) — short code
  • is_active (boolean)
  • company_type (int) — 1=Customer, 2=Lead, 3=Prospect, 4=Dead, 6=Cancellation, 7=Vendor, etc.
  • owner_resource_id (bigint) — account manager, FK to resources.id
  • classification (varchar) — e.g. "Platinum", "Gold", etc.
  • Address fields: address1, city, state, postal_code
  • last_activity_date (timestamp)

contacts (~4.2K rows, 37 columns)

People at client companies.

Key columns:

  • id (bigint PK)
  • first_name, last_name, email_address, phone (varchar)
  • company_id (bigint) — FK to companies.id
  • is_active (boolean)
  • title (varchar) — job title

resources (~40 columns)

Internal staff / technicians.

Key columns:

  • id (bigint PK)
  • first_name, last_name, email (varchar)
  • email_address (varchar) — primary email
  • is_active (boolean)
  • resource_type (varchar)
  • default_service_desk_role_id (bigint)
  • hire_date (date)
  • location_id (bigint)

time_entries (~154K rows, 45 columns)

Work logged against tickets, tasks, or projects.

Key columns:

  • id (bigint PK)
  • resource_id (bigint) — who did the work, FK to resources.id
  • ticket_id (bigint) — FK to tickets.id (NULL if task/project entry)
  • task_id (bigint) — FK to tasks.id
  • project_id (bigint) — FK to projects.id
  • company_id (bigint) — FK to companies.id
  • entry_date (timestamp) — date of work
  • hours_worked (numeric) — actual hours
  • hours_to_bill (numeric) — billable hours
  • start_date_time, end_date_time (timestamp) — clock in/out
  • title (varchar), notes (text), internal_notes (text)
  • billable (boolean), non_billable (boolean)
  • billing_rate (numeric), cost_rate (numeric), revenue (numeric)
  • contract_id (bigint), contract_service_id (bigint)
  • role_id (bigint)
  • is_deleted (boolean)

ticket_notes (~30K rows, 14 columns)

Notes/comments on tickets.

Key columns:

  • id (bigint PK)
  • ticket_id (bigint) — FK to tickets.id
  • title (varchar), description (text) — note content
  • note_type (int) — internal, external, etc.
  • publish (int) — visibility
  • creator_resource_id (bigint) — who wrote it
  • create_date_time (timestamptz)

configuration_items (~7K rows, 95 columns)

Devices/assets tracked in Autotask.

Key columns:

  • id (bigint PK)
  • reference_title (varchar) — device name (e.g. "DT037", "SRV-DC01")
  • reference_number (varchar) — often a GUID from RMM
  • serial_number (varchar)
  • company_id (bigint) — FK to companies.id
  • contact_id (bigint) — FK to contacts.id
  • is_active (boolean)
  • device_type (varchar)

Note: reference_title follows a naming convention per client (e.g. DT037 exists at multiple companies as separate CIs). Always filter by both reference_title AND company_id when searching.

contracts (~44 columns)

Service agreements with clients.

Key columns:

  • id (bigint PK)
  • company_id (bigint) — FK to companies.id
  • contract_name (varchar), contract_number (varchar)
  • contract_type (int), status (int)
  • start_date, end_date (date)
  • estimated_hours (numeric), estimated_revenue (numeric)

contract_services (~8.2K rows)

Line items on contracts.

  • contract_idcontracts.id
  • company_idcompanies.id
  • service_name (text), unit_price, quantity

projects (~291 rows, 36 columns)

Key columns:

  • id, company_id, project_name, status, type
  • project_lead_resource_idresources.id
  • start_date_time, end_date_time, actual_hours, estimated_time

tasks (~4.3K rows, 34 columns)

Tasks on tickets or projects.

  • ticket_idtickets.id
  • project_idprojects.id
  • assigned_resource_idresources.id
  • status, priority, estimated_hours, remaining_hours

billing_items (~96K rows)

Invoice line items linked to tickets, tasks, or projects.

  • ticket_idtickets.id, task_idtasks.id, project_idprojects.id
  • company_idcompanies.id
  • quantity, rate, total_amount, unit_cost, unit_price

2. Lookup / Picklist Tables

These map integer codes to human-readable labels. Join on value.

statuses (ticket statuses)

Value Label
1 New
5 Complete
7 Waiting Customer
8 In Progress
10 Dispatched
11 Escalate
12 Waiting Vendor
13 Waiting Approval
14 Resource Assigned
16 Reopened
19 End User Note Added
25 On Hold
48 Escalate to MC
54 Resolved <CSAT Survey>
57 Escalate to Wulf

priorities

Value Label
1 Standard
2 Medium
4 Critical
6 High
7 Security Event
8 Minor Service
9 Major Service
11 Fast Track

queues (46 active)

Major queues include:

  • 29682833 Level 1 Support
  • 29682969 Level 2 Support
  • 29703428 Level 3 Support
  • 29749490 Client Success
  • 8 Monitoring Alert
  • 29832283 Operations Triage
  • 5 Client Triage
  • 29853700 Deployment
  • 29853698 Project Delivery
  • 29853701 IT Operations
  • 29853699 Mission Control
  • Client-specific queues: TTG, LEC, PER, VCF, Premier Automation, TNT Pizza, Trivium Packaging, Glunt

3. Datto RMM

datto_rmm_alerts (~4.3K rows, 61 columns)

  • id (int PK), uid (text) — alert identifiers
  • alert_category, alert_type, alert_message_en — what triggered
  • priority (text) — Critical, High, Moderate, Low, Information
  • resolved (boolean), resolved_on (timestamptz)
  • muted (boolean)
  • ticket_number (text) — linked Autotask ticket
  • device_hostname, device_ip, device_os, device_id
  • site_id (text) — FK to datto_rmm_sites
  • timestamp (timestamptz) — when alert fired

datto_rmm_devices (~3.6K rows, 42 columns)

  • id (int PK), uid (text), hostname
  • device_type_category (text) — Server, Desktop, Laptop, Network Device
  • operating_system, domain, int_ip_address, ext_ip_address
  • online (boolean), last_seen (timestamptz)
  • last_logged_in_user (text)
  • antivirus_product, antivirus_status, patch_status
  • site_id (int) — FK to datto_rmm_sites.id
  • udf (jsonb) — custom fields

datto_rmm_sites (~16 columns)

  • id (int PK), uid, name
  • autotask_company_id (int) — FK to companies.id (links RMM sites to Autotask clients)
  • autotask_company_name
  • number_of_devices, number_of_online_devices

Join pattern: datto_rmm_sites.autotask_company_id = companies.id


4. SentinelOne

s1_agents (~2.8K rows, 42 columns)

Endpoint security agents.

  • id (varchar PK) — S1 agent ID
  • computer_name, os_name, os_type
  • site_ids1_sites.id, site_name
  • is_active, is_decommissioned
  • infected (boolean), active_threats (int)
  • network_status, mitigation_mode, detection_state
  • external_ip, last_active_date, last_logged_in_user_name
  • firewall_enabled (boolean)

s1_threats (~4.1K rows, 25 columns)

Detected threats.

  • id (varchar PK)
  • threat_name, classification, confidence_level
  • mitigation_status, analyst_verdict, incident_status
  • agent_ids1_agents.id
  • agent_computer_name, agent_os_name
  • site_ids1_sites.id

s1_sites (~22 columns)

  • id (varchar PK), name, account_name
  • health_status, active_licenses, total_licenses

s1_company_mappings

Maps S1 sites to Autotask companies for cross-referencing.


5. Veeam Backup

veeam_organizations (~17 columns)

  • instance_uid (PK), name, company_id
  • All other Veeam tables FK to veeam_organizations.instance_uid

veeam_backup_jobs (~234 rows)

  • instance_uid, name, type, status, last_run, next_run
  • organization_uidveeam_organizations
  • backup_server_uidveeam_backup_servers

veeam_backup_agents (~727 rows)

  • Backup agents installed on endpoints
  • organization_uidveeam_organizations

veeam_alarms (~581 rows)

  • Active alarms/alerts
  • organization_uidveeam_organizations

veeam_protected_workloads, veeam_repositories, veeam_backup_servers

Supporting tables for backup infrastructure.


6. IT Glue Documentation

itg_organizations (~330 rows)

  • id (bigint PK), name, short_name, organization_type_name
  • psa_id (varchar) — Autotask company ID (string). Join: itg_organizations.psa_id::bigint = companies.id

itg_configurations (~14.7K rows)

Hardware/software assets documented in IT Glue.

  • id, organization_iditg_organizations.id
  • name, hostname, serial_number, asset_tag
  • configuration_type_name, configuration_status_name
  • primary_ip, mac_address, operating_system
  • warranty_expires_at, installed_at

itg_passwords (~17 columns)

  • id, organization_id, name, username, password_category_name
  • url, notes

itg_flexible_assets (~3.2K rows)

Custom documentation (e.g. Backup configs, Email configs, LAN/VLAN, Voice/PBX).

  • id, organization_id, flexible_asset_type_id, flexible_asset_type_name
  • traits (jsonb) — all custom field values

itg_contacts, itg_documents, itg_expirations, itg_domains, itg_locations

Supporting IT documentation tables.


7. Engagement & Communications

graph_users

Microsoft 365 users synced from Azure AD.

  • id (varchar PK), display_name, email, job_title, department
  • account_enabled (boolean)

teams_meetings (~14.3K rows)

Teams calendar events / meetings.

  • id (int PK), user_email, subject
  • start_time, end_time (timestamptz), duration_minutes
  • attendee_count, client_attendee_count, has_client_attendees (boolean)

teams_meeting_attendees (~12.6K rows)

  • meeting_idteams_meetings.id
  • attendee_email, attendee_name
  • matched_contact_idcontacts.id
  • matched_company_idcompanies.id

engagement_snapshots (~1K rows)

Weekly/monthly aggregates of M365 activity per user.

  • user_email, period_type (D7, D30, D90, D180)
  • teams_chat_messages, teams_calls, teams_meetings_attended, teams_meetings_organized
  • emails_sent, emails_received, emails_read

zoom_meetings, zoom_meeting_participants

  • host_email, topic, start_time, end_time, duration_minutes
  • Participants with matched_contact_idcontacts.id, matched_company_idcompanies.id
  • is_internal (boolean) — internal vs external attendee

zoom_calls (~2.8K rows)

  • resource_email, direction (inbound/outbound), call_status
  • other_party_number, other_party_name
  • matched_contact_id, matched_company_id

8. Common Join Patterns

-- Ticket with company, resource, and status label
SELECT t.id, t.title, c.company_name, 
       r.first_name || ' ' || r.last_name AS technician,
       s.label AS status_label, p.label AS priority_label
FROM tickets t
LEFT JOIN companies c ON c.id = t.company_id
LEFT JOIN resources r ON r.id = t.assigned_resource_id
LEFT JOIN statuses s ON s.value = t.status
LEFT JOIN priorities p ON p.value = t.priority
WHERE t.is_deleted IS NOT TRUE;

-- Time entries for a ticket
SELECT te.entry_date, te.hours_worked, te.notes,
       r.first_name || ' ' || r.last_name AS technician
FROM time_entries te
JOIN resources r ON r.id = te.resource_id
WHERE te.ticket_id = $1 AND te.is_deleted IS NOT TRUE;

-- RMM device → Autotask company
SELECT d.hostname, d.device_type_category, d.operating_system,
       s.autotask_company_name, d.online, d.last_seen
FROM datto_rmm_devices d
JOIN datto_rmm_sites s ON s.id = d.site_id;

-- Config item lookup (always filter by company too)
SELECT ci.id, ci.reference_title, ci.serial_number, c.company_name
FROM configuration_items ci
JOIN companies c ON c.id = ci.company_id
WHERE ci.reference_title = 'DT037' AND ci.company_id = $1;

-- IT Glue org → Autotask company
SELECT ig.name, ig.id AS itg_org_id, c.id AS autotask_company_id, c.company_name
FROM itg_organizations ig
JOIN companies c ON ig.psa_id::bigint = c.id;

-- Meetings with client attendees
SELECT tm.subject, tm.start_time, tm.duration_minutes,
       tma.attendee_name, c.company_name
FROM teams_meetings tm
JOIN teams_meeting_attendees tma ON tma.meeting_id = tm.id
LEFT JOIN companies c ON c.id = tma.matched_company_id
WHERE tm.has_client_attendees = true;

9. Important Notes

  1. Soft deletes: Most Autotask tables have is_deleted (boolean) and deleted_at. Always add WHERE is_deleted IS NOT TRUE unless you want deleted records.

  2. Picklist joins: status, priority, queue_id, source on tickets are integer codes. Join to statuses, priorities, queues on .value for labels.

  3. Configuration item names are NOT unique globally. Names like "DT037" are a per-client naming convention. Always pair with company_id.

  4. Timestamps: Most Autotask timestamps are timestamp without time zone stored in UTC. Teams/Zoom timestamps are timestamp with time zone.

  5. Monitor tickets: tickets.source = 8 indicates RMM-generated tickets. monitor_id links to the specific Datto RMM monitor. These represent ~74% of all tickets.

  6. Cross-platform linking:

    • RMM → Autotask: datto_rmm_sites.autotask_company_id = companies.id
    • IT Glue → Autotask: itg_organizations.psa_id::bigint = companies.id
    • S1 → Autotask: via s1_company_mappings
    • Zoom/Teams → Contacts: matched_contact_id / matched_company_id columns
    • Config Items → RMM: configuration_items.reference_number sometimes matches RMM device UIDs
  7. Row counts (as of March 2026): tickets 109K, time_entries 154K, billing_items 96K, ticket_notes 30K, teams_meetings 14K, itg_configurations 15K, configuration_items 7K, companies 4K, contacts 4.2K, datto_rmm_devices 3.6K, datto_rmm_alerts 4.3K, s1_agents 2.8K, s1_threats 4.1K, zoom_calls 2.8K.