wulfclaw/PULSE_DATABASE_SKILL.md
Claude b25557a310 docs: add project_phases table, fix tasks.project_id resolution
- Add project_phases section: schema, purpose, why it exists
- Document the tasks.project_id backfill mechanism (Autotask Tasks API
  omits projectID; resolved post-sync via phase_id join)
- Add query for tasks whose parent project is archived (project_id NULL
  but resolvable via project_phases)
- Update domain table to include project_phases (~2.7K rows)
2026-03-24 13:53:58 -04:00

36 KiB
Raw Blame History

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
  • Timezone: PostgreSQL server runs in UTC. All timestamp without time zone columns are UTC. Convert to ET with AT TIME ZONE 'America/New_York'.

Data Domains at a Glance

Domain Key Tables Approx Rows Description
Autotask PSA tickets, time_entries, companies, contacts, resources, configuration_items, contracts, projects, project_phases, tasks, ticket_notes 152K tickets, 156K time entries, 7K CIs, 2.7K phases Service desk, billing, contracts, clients
Datto RMM datto_rmm_alerts, datto_rmm_devices, datto_rmm_sites 21K alerts, 3.6K devices Remote monitoring & management
Auvik auvik_tenants, auvik_tenant_mappings 17 tenants, 15 mappings Network management — device inventory fetched live from API; tenant/company links stored in DB
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 IT documentation
Microsoft 365 graph_users, teams_meetings, teams_meeting_attendees, engagement_snapshots 14.4K meetings Teams meetings, activity reports
Zoom zoom_users, zoom_meetings, zoom_meeting_participants, zoom_calls 2.8K calls Zoom calls and meetings
QuickBooks Online qbo_invoices, qbo_payments, qbo_deposits, qbo_transactions, qbo_reports 6.9K invoices, 12.1K txns Accounting — invoices, payments, deposits, financial reports
Zabbix NMS zabbix_events, zabbix_wan_hosts 231 events, 75 hosts Network monitoring — WAN hosts, alert events
Billing billing_items 96K items Invoice line items tied to tickets/projects/tasks
Authentication "user", session, account, verification, two_factor 3 users Better Auth — Entra ID SSO, RBAC, sessions

1. Autotask PSA — Core Service Desk

tickets (~152K rows, 68 columns)

Timezone note: create_date, due_date_time, completed_date, resolved_date_time, first_response_date_time, last_activity_date are all timestamp without time zone stored in UTC. To display in ET: create_date AT TIME ZONE 'America/New_York'.

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
8 Monitoring Alert (RMM/Datto)
4 Email
21 Portal
2 Phone/Voice
-1 Insourced
-2 Outsourced
35 Phish Alert
6 API
17 Internal Alert

Ticket types (tickets.ticket_type):

Value Meaning
1 Incident
2 Service Request
5 Alert
NULL Unclassified

companies (~246 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 (~41K 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 (~314 rows, 36 columns)

Planned engagements for client work — distinct from reactive service tickets. A project has a defined scope, timeline, and budget. Think of projects as the planned/scheduled side of the business (switch deployments, onboarding, infrastructure upgrades) while tickets are the reactive/support side.

Key columns:

  • id (bigint PK) — Autotask project ID
  • company_id (bigint) — FK to companies.id
  • project_name (varchar) — display name
  • project_number (varchar) — unique identifier string
  • description (text)
  • status (int) — project status code
  • type (int) — project type
  • project_lead_resource_id (bigint) — FK to resources.id
  • owner_resource_id (bigint) — FK to resources.id
  • start_date_time, end_date_time (timestamp) — scheduled window
  • completed_date_time (timestamp)
  • estimated_time (numeric) — estimated hours
  • actual_hours (numeric) — hours logged so far
  • estimated_sale_cost, labor_estimated_revenue (numeric) — financial estimates
  • completed_percentage (numeric) — 0100
  • last_activity_date_time (timestamp)
  • is_deleted (boolean)

Relationship to tickets: A ticket can optionally be linked to a project via tickets.project_id. This is typically used when a ticket was generated as part of project work (e.g. a change request within a project).

project_phases (~2.7K rows, 19 columns)

Phases group tasks within a project (e.g. "Equipment Selection", "Installation", "Testing"). Every phase belongs to exactly one project.

Key columns:

  • id (bigint PK) — Autotask phase ID (shares namespace with tasks)
  • project_id (bigint) — FK to projects.id (no FK constraint — may reference archived projects)
  • parent_phase_id (bigint) — parent phase for nested phase trees
  • title (varchar) — phase name
  • description (text)
  • phase_number (varchar) — e.g. T20251222.0116
  • estimated_hours (numeric)
  • start_date_time, due_date (timestamp) — scheduled window
  • create_date_time, last_activity_date_time (timestamp)
  • is_scheduled (boolean)
  • creator_resource_id (bigint) — FK to resources.id
  • is_deleted (boolean)

Why this table matters: The Autotask Tasks bulk API omits projectID from its response. After each project_phases sync, an automatic backfill updates tasks.project_id by joining tasks.phase_id = project_phases.id. This is the only way to resolve which project a task belongs to.

Join to get a task's project via phase:

SELECT t.id, t.title, pp.title AS phase, p.project_name
FROM tasks t
JOIN project_phases pp ON pp.id = t.phase_id
LEFT JOIN projects p ON p.id = pp.project_id
WHERE t.is_deleted IS NOT TRUE;

tasks (~5K rows, 34 columns)

Tasks are discrete units of work that live inside a project or a ticket. They are NOT standalone items — every task belongs to either a project (project_id) or a ticket (ticket_id), not both simultaneously.

Key difference from tickets:

  • Tickets = reactive service desk items. Created by clients, RMM alerts, email, or portal. Queued and triaged.
  • Tasks = planned steps within a project (or checklist items on a ticket). Created by technicians and project managers. Have structured phases and scheduling.

Real-world examples of tasks:

  • A "Switch Deployment" project might have tasks: Switch [Config] - ynghynswp27, Switch [Build] - ynghynswp27, Switch [Deploy] - ynghynswp27
  • Each task represents a discrete phase of work, assigned to a resource with estimated hours

Key columns:

  • id (bigint PK) — Autotask task ID
  • title (varchar) — task name
  • description (text)
  • project_id (bigint) — FK to projects.id (if this is a project task)
  • ticket_id (bigint) — FK to tickets.id (if this is a ticket sub-task)
  • phase_id (bigint) — project phase grouping (project tasks only)
  • assigned_resource_id (bigint) — FK to resources.id
  • creator_resource_id (bigint) — FK to resources.id
  • status (int), priority (int)
  • task_type (int)
  • task_number (varchar) — human-readable number within the project
  • estimated_hours (numeric), remaining_hours (numeric)
  • start_date_time, end_date_time (timestamp) — scheduled window
  • completed_date_time (timestamp)
  • task_is_billable (boolean)
  • is_deleted (boolean)

Time entries on tasks: time_entries.task_id links work logs to specific tasks. Task time entries also carry project_id and company_id for quick aggregation without joins.

Known sync behaviour — project_id population: The Autotask bulk Tasks API does not return projectID in its response. project_id is instead populated post-sync via a backfill that joins tasks.phase_id → project_phases.id → project_phases.project_id. This backfill runs automatically every time project_phases is synced. Tasks whose parent project is completed/archived (and therefore absent from our projects table) will still have project_id = NULL — use phase_id → project_phases.project_id directly in those cases.

Finding project tasks vs ticket tasks:

-- Project tasks only (project_id populated via phase backfill)
SELECT t.*, p.project_name FROM tasks t
JOIN projects p ON p.id = t.project_id
WHERE t.project_id IS NOT NULL AND t.is_deleted IS NOT TRUE;

-- Tasks whose project is archived (project_id NULL but phase_id resolvable)
SELECT t.*, pp.project_id AS phase_project_id, pp.title AS phase_title
FROM tasks t JOIN project_phases pp ON pp.id = t.phase_id
WHERE t.project_id IS NULL AND t.is_deleted IS NOT TRUE;

-- Ticket sub-tasks only
SELECT t.*, tk.title AS ticket_title FROM tasks t
JOIN tickets tk ON tk.id = t.ticket_id
WHERE t.ticket_id IS NOT NULL AND t.is_deleted IS NOT TRUE;

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)

Join: statuses.value = tickets.status

Value Label
1 New
5 Complete
7 Waiting Customer
8 In Progress
9 Waiting Materials
10 Dispatched
11 Escalate
12 Waiting Vendor
13 Waiting Approval
14 Resource Assigned
15 Ready to Deploy
16 Reopened
17 Info Req
19 End User Note Added
20 Equipment Pulled
21 Waiting Verification
25 On Hold
27 Resolution Plan
30 Service Call Scheduled
37 Client Non-Responsive
45 Pending Next Site Visit
46 Burn-in In Progress
47 Resource Requested
48 Escalate to MC
51 Reconcile Billing
54 Resolved <CSAT Survey>
55 Huddle Review
56 Tracking Shipment
57 Escalate to Wulf
58 Pre-Pick Order
59 Need to Order/Fulfill
60 Loading Customer Config
61 Escalate to CSM
62 Escalate to Customer HD
64 Waiting Trivium Employee
65 Waiting Procurement
66 Internal Note Added
67 Quote Delivered
68 Ready for Customer Config
69 Outsourced
70 Waiting Quote Acceptance
71 Waiting Project/Ticket

priorities

Join: priorities.value = tickets.priority

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

queues (46 active)

Join: queues.value = tickets.queue_id

Value Label
5 Client Triage
6 Post Sale
8 Monitoring Alert
29682833 Level 1 Support
29682969 Level 2 Support
29703428 Level 3 Support
29749490 Client Success
29793481 App-Care
29807035 Alert II
29807036 Alert I
29832283 Operations Triage
29853695 Follow Up
29853697 Trivium Packaging - Help Desk
29853698 Project Delivery
29853699 Mission Control
29853700 Deployment
29853701 IT Operations
29853702 Recurring Tickets
29853706 Premier Automation Help Desk
29853710 TTG Help Desk
29853714 Glunt Help Desk
29853719 TNT Pizza Help Desk
29853720 TTG Machines
29853721 TTG NetOps
29853723 TTG Finance
29853724 TTG Safety
29853726 TTG Remote
29853727 Purchasing
29853728 TTG New User Access
29853729 TTG Term User Access
29853731 Trivium Packaging - MES
29853738 TTG Security
29853740 TTG Change Mgmt
29853741 Subcontractor
29853750 PER Service Desk
29853751 PER Network Operations Center
29853752 PER Security Operations Center
29853753 LEC Service Desk
29853754 LEC Network Operations Center
29853755 LEC Security Operations Center
29853756 VCF Service Desk
29853757 VCF Network Operations
29853758 VCF Security Operations Center
29853766 Sales
29780304 TaskFire
29753333 Waiting Verification

issue_types

Join: issue_types.value = tickets.issue_type

Value Label
4 Upgrade
6 New Install
7 Monitoring Alert
10 Break/Fix
11 Maintenance
12 Help Desk
13 Email
14 Vendor
15 User Education
16 Provision
18 Purchase
19 Deploy
20 Reactive
21 MAC
22 Centralized Services
23 NOC Services
25 Networking
27 WiFi
28 Client Success
29 Automation
30 Server
31 Hardware
32 LOB Software
33 Workstation
36 24-Hour Emergency Support

3. Datto RMM

datto_rmm_alerts (~20.7K 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. Auvik Network Management

Auvik is the network device management platform used to monitor and manage client network infrastructure (switches, routers, firewalls, APs, etc.). It organizes clients as tenants — one tenant per client organization.

Important: Auvik device inventory is NOT stored in the Pulse database. Device data (hostnames, IPs, device types, configs, firmware) is fetched live from the Auvik API at query time. Only the tenant list and their mappings to Autotask companies are persisted in the DB.

auvik_tenants (~17 rows)

The list of Auvik tenants (client organizations) known to this Pulse instance.

Key columns:

  • id (int PK) — internal DB ID
  • tenant_id (varchar) — Auvik's unique tenant identifier (numeric string, e.g. "197643669426944326")
  • tenant_name (varchar) — short name / domain prefix (e.g. "seubert", "wulfconsulting")
  • domain_prefix (varchar) — same as tenant_name; used in Auvik URLs
  • device_count (int) — last known device count from sync
  • last_sync_at (timestamptz) — when this tenant was last synced
  • created_at, updated_at (timestamptz)

Sample tenants: vollmer, poha, premierautomation, wulfconsulting, vorteqcoilfinishers, seubert, hynesindustries, greco, therla, and ~8 others.

auvik_tenant_mappings (~15 rows)

Maps each Auvik tenant to its corresponding Autotask company. This is the cross-platform link between network management and the PSA.

Key columns:

  • id (int PK)
  • auvik_tenant_id (varchar) — Auvik tenant ID, matches auvik_tenants.tenant_id
  • auvik_tenant_name (varchar) — short name
  • autotask_company_id (int) — FK to companies.id
  • autotask_company_name (varchar) — denormalized for convenience
  • created_at, updated_at (timestamptz)

Join pattern — Auvik tenant → Autotask company:

SELECT atm.auvik_tenant_name, c.company_name, c.id AS autotask_company_id
FROM auvik_tenant_mappings atm
JOIN companies c ON c.id = atm.autotask_company_id;

What Auvik tracks (via live API, not DB):

  • Network device inventory: switches, routers, firewalls, access points, printers, UPS units
  • Device details: hostname, IP addresses, MAC addresses, model, firmware version, vendor
  • Online/offline status and last seen time
  • Device configurations (running config snapshots — see /api/auvik/device-config endpoint)
  • Network topology and interface relationships

Auvik device naming convention: Hostnames in Auvik follow the pattern {client_prefix}{device_type}{sequence}, e.g. ynghynswp27 = ynghy (client) + nsw (network switch) + p27 (port count / unit). These hostnames also appear in Autotask ticket notes and task titles when work is performed on the device.


6. 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.


7. 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.


8. 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.


9. 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

10. QuickBooks Online (Accounting)

qbo_invoices (~6.9K rows)

  • id (text PK) — QBO invoice ID
  • doc_number (text) — invoice number (e.g. "1042")
  • txn_date (date), due_date (date)
  • customer_ref_id, customer_ref_name — QBO customer
  • total_amt (numeric), balance (numeric) — amounts
  • status (text) — Paid, Overdue, etc.
  • line_items (jsonb) — invoice line detail
  • linked_txns (jsonb) — linked payments

qbo_payments (~4.5K rows)

  • id (text PK)
  • txn_date (date), total_amt (numeric)
  • customer_ref_id, customer_ref_name
  • payment_method_ref (text), deposit_account_ref (text)
  • unapplied_amt (numeric)
  • linked_txns (jsonb) — linked invoices

qbo_deposits (~1.8K rows)

  • id (text PK)
  • txn_date (date), total_amt (numeric)
  • deposit_to_account_ref_id, deposit_to_account_ref_name
  • line_items (jsonb)

qbo_transactions (~12.1K rows)

General ledger transactions (expenses, bills, journal entries, etc.).

  • id (text), txn_type (text) — composite PK
  • txn_date (date), doc_number (text)
  • entity_ref_id, entity_ref_name, entity_type — vendor/customer
  • account_ref_id, account_ref_name — GL account
  • total_amt (numeric)
  • line_items (jsonb)

Transaction types: Bill, BillPayment, Expense, JournalEntry, Transfer, VendorCredit, CreditMemo, SalesReceipt, Estimate, PurchaseOrder, etc.

qbo_reports (~36 rows)

Periodic financial reports stored as JSON.

  • id (serial PK)
  • report_type (text) — ProfitAndLoss, BalanceSheet, CashFlow
  • period_start (date), period_end (date)
  • report_data (jsonb) — full QBO report payload
  • Unique on (realm_id, report_type, period_start, period_end)

11. Zabbix NMS

zabbix_wan_hosts (~75 rows)

WAN monitoring hosts auto-synced from RMM site public IPs.

  • host_id (text) — Zabbix host ID
  • host (text) — hostname in Zabbix
  • name (text) — display name
  • ip (text) — WAN IP address
  • status (int) — 0=enabled, 1=disabled
  • company_id (bigint) — FK to companies.id
  • rmm_site_id (int) — FK to datto_rmm_sites.id

zabbix_events (~228 rows)

Zabbix alert events.

  • event_id (text PK)
  • host_id (text), host_name (text)
  • trigger_id (text), trigger_name (text)
  • severity (int) — 0=Not classified, 1=Info, 2=Warning, 3=Average, 4=High, 5=Disaster
  • value (int) — 0=OK, 1=Problem
  • clock (timestamptz) — event time

12. Authentication (Better Auth + Entra ID)

"user" table (~3 rows)

Note: Table name is "user" (quoted) — must be quoted in SQL.

  • id (text PK)
  • name (text), email (text UNIQUE)
  • "emailVerified" (boolean), image (text)
  • role (text) — super-admin, admin, user
  • banned (boolean), "bannedReason" (text), "banExpires" (timestamp)
  • requires_setup (boolean), "twoFactorEnabled" (boolean)
  • "createdAt", "updatedAt" (timestamp)

Note: Better Auth uses camelCase column names — must be double-quoted in raw SQL.

session

  • id (text PK), "userId""user".id
  • token (text UNIQUE), "expiresAt" (timestamp)
  • "ipAddress", "userAgent" (text)

account

OAuth provider links (Microsoft Entra ID).

  • id (text PK), "userId""user".id
  • "providerId" (text) — e.g. microsoft
  • "accountId" (text) — provider-specific user ID
  • "accessToken", "refreshToken", "idToken" (text)

13. 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;

-- QBO: Revenue by month (from invoices)
SELECT DATE_TRUNC('month', txn_date) AS month, 
       SUM(total_amt) AS total_invoiced, COUNT(*) AS invoice_count
FROM qbo_invoices
GROUP BY 1 ORDER BY 1 DESC;

-- QBO: Outstanding balances by customer
SELECT customer_ref_name, SUM(balance) AS outstanding
FROM qbo_invoices WHERE balance > 0
GROUP BY 1 ORDER BY 2 DESC;

-- Zabbix: Active WAN problems with company
SELECT ze.host_name, ze.trigger_name, ze.severity, ze.clock,
       c.company_name
FROM zabbix_events ze
JOIN zabbix_wan_hosts zwh ON zwh.host_id = ze.host_id
LEFT JOIN companies c ON c.id = zwh.company_id
WHERE ze.value = 1 ORDER BY ze.clock DESC;

-- Project with all its tasks and hours logged
SELECT p.project_name, p.status, p.actual_hours,
       t.title AS task_title, t.status AS task_status,
       t.estimated_hours, t.remaining_hours,
       r.first_name || ' ' || r.last_name AS assigned_to
FROM projects p
LEFT JOIN tasks t ON t.project_id = p.id AND t.is_deleted IS NOT TRUE
LEFT JOIN resources r ON r.id = t.assigned_resource_id
WHERE p.id = $1 AND p.is_deleted IS NOT TRUE;

-- All time entries for a project (direct + via tasks)
SELECT te.entry_date, te.hours_worked, te.notes,
       r.first_name || ' ' || r.last_name AS technician,
       t.title AS task_title,
       te.billable
FROM time_entries te
JOIN resources r ON r.id = te.resource_id
LEFT JOIN tasks t ON t.id = te.task_id
WHERE te.project_id = $1 AND te.is_deleted IS NOT TRUE
ORDER BY te.entry_date;

-- Hours by technician across all projects this month
SELECT r.first_name || ' ' || r.last_name AS technician,
       SUM(te.hours_worked) AS total_hours,
       SUM(CASE WHEN te.billable THEN te.hours_worked ELSE 0 END) AS billable_hours
FROM time_entries te
JOIN resources r ON r.id = te.resource_id
WHERE te.project_id IS NOT NULL
  AND te.entry_date >= DATE_TRUNC('month', NOW())
  AND te.is_deleted IS NOT TRUE
GROUP BY 1 ORDER BY 2 DESC;

-- Auvik tenant → Autotask company → open tickets
SELECT atm.auvik_tenant_name, c.company_name,
       COUNT(tk.id) AS open_tickets
FROM auvik_tenant_mappings atm
JOIN companies c ON c.id = atm.autotask_company_id
LEFT JOIN tickets tk ON tk.company_id = c.id
  AND tk.status NOT IN (5) -- not Complete
  AND tk.is_deleted IS NOT TRUE
GROUP BY 1, 2 ORDER BY 3 DESC;

14. 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 — all UTC: The PostgreSQL server timezone is UTC. All timestamp without time zone columns (Autotask) are stored in UTC. timestamp with time zone columns (Teams, Zoom, QBO) are also UTC-normalized. To display in Eastern Time: column AT TIME ZONE 'America/New_York'. Example: create_date AT TIME ZONE 'America/New_York'.

  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
    • Auvik → Autotask: auvik_tenant_mappings.autotask_company_id = companies.id
    • Zoom/Teams → Contacts: matched_contact_id / matched_company_id columns
    • Config Items → RMM: configuration_items.reference_number sometimes matches RMM device UIDs
    • Auvik hostnames → Autotask tickets/tasks: search ticket_notes.description and tasks.title for the device hostname (e.g. ynghynswp27) to find all work done on a network device
  7. Tickets vs Projects vs Tasks — when to use which:

    • Query tickets when asking about: support requests, break/fix, helpdesk, RMM alerts, client-reported issues, SLA performance, response times, queue volumes
    • Query projects when asking about: planned work, deployments, onboarding, infrastructure upgrades, project status, project hours/budget
    • Query tasks when asking about: individual steps within a project, task completion rates, who is assigned to what within a project, task-level time tracking. Tasks have both project_id (project tasks) and ticket_id (ticket sub-tasks) — check which is populated.
    • Query time_entries for actual hours worked — it spans all three: ticket_id, task_id, and project_id can all be non-null depending on context
  8. Row counts (as of March 24, 2026): tickets 152K, time_entries 156K, billing_items 96K, ticket_notes 42K, teams_meetings 14.4K, itg_configurations 14.7K, configuration_items 7K, tasks 5K, projects 314, companies 246, contacts 4.2K, datto_rmm_devices 3.6K, datto_rmm_alerts 21K, s1_agents 2.8K, s1_threats 4.1K, auvik_tenants 17, auvik_tenant_mappings 15, zoom_calls 2.8K, qbo_invoices 6.9K, qbo_transactions 12.1K, qbo_payments 4.5K, qbo_deposits 1.8K, qbo_reports 36, zabbix_wan_hosts 75, zabbix_events 231.