157 lines
6.7 KiB
Markdown
157 lines
6.7 KiB
Markdown
|
|
# Autotask Sync — Reference Guide
|
|||
|
|
|
|||
|
|
## Overview
|
|||
|
|
|
|||
|
|
Pulse maintains a local PostgreSQL mirror of Autotask data in the `pulse_autotask` database. Three mechanisms keep it current: **scheduled syncs**, **real-time webhooks**, and **manual/API-triggered syncs**.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Automatic Syncs (Scheduled)
|
|||
|
|
|
|||
|
|
All schedules are managed via the **Admin → Sync Scheduler** UI or directly in the `sync_schedules` table. They can be enabled/disabled individually.
|
|||
|
|
|
|||
|
|
| Schedule ID | Name | Cron (UTC) | Local (EST) | Type | Enabled |
|
|||
|
|
|---|---|---|---|---|---|
|
|||
|
|
| `sync-6am` | Morning Sync | `0 11 * * *` | 6:00 AM daily | incremental | ✅ |
|
|||
|
|
| `sync-830am` | Morning Sync | `30 13 * * *` | 8:30 AM daily | incremental | ✅ |
|
|||
|
|
| `sync-11am` | Midday Sync | `0 16 * * *` | 11:00 AM daily | incremental | ✅ |
|
|||
|
|
| `sync-230pm` | Afternoon Sync | `30 19 * * *` | 2:30 PM daily | incremental | ✅ |
|
|||
|
|
| `weekly-full` | Weekly Full Sync | `0 3 * * 0` | 3:00 AM Sunday | full | ✅ |
|
|||
|
|
| `contract-services` | Contract Services | `0 4 * * *` | 4:00 AM daily | contract-services | ✅ |
|
|||
|
|
|
|||
|
|
### Incremental vs Full
|
|||
|
|
|
|||
|
|
**Incremental sync** — pulls only records modified since the last successful sync timestamp (stored in `sync_history`). Much faster. Runs 4× per day.
|
|||
|
|
|
|||
|
|
**Full sync** — pulls everything regardless of modification date, going back `yearsBack` years (default: 2). Runs every Sunday at 3 AM. Also used for initial setup or repair.
|
|||
|
|
|
|||
|
|
### What incremental syncs cover
|
|||
|
|
|
|||
|
|
Every incremental run syncs all entities in dependency order:
|
|||
|
|
|
|||
|
|
1. Companies, Resources, Statuses, Issue Types, Sub-Issue Types, Work Types, Queues, Priorities, Ticket Categories *(no dependencies)*
|
|||
|
|
2. Contacts *(requires Companies)*
|
|||
|
|
3. Projects *(requires Companies, Resources)*
|
|||
|
|
4. Tickets *(requires Companies, Resources, Contacts)*
|
|||
|
|
5. Tasks *(requires Resources, Projects, Tickets)*
|
|||
|
|
6. Configuration Items *(requires Companies, Contacts)*
|
|||
|
|
7. Contracts *(requires Companies, Contacts)*
|
|||
|
|
8. Autotask Services *(standalone)*
|
|||
|
|
9. Billing Items *(requires Companies, Tasks, Tickets, Projects)*
|
|||
|
|
10. Time Entries *(requires Companies, Resources, Contacts, Projects, Tasks, Tickets)*
|
|||
|
|
11. Ticket Notes *(requires Tickets)*
|
|||
|
|
12. Tag Groups, Tags, Ticket Tag Associations
|
|||
|
|
|
|||
|
|
> **Note:** Companies and Resources do not support date-based filtering in the Autotask API, so they always do a full pull on every run (fast — ~57 resources, ~236 companies).
|
|||
|
|
|
|||
|
|
### Time-windowed entities
|
|||
|
|
|
|||
|
|
Some entities are too large to sync in full and are filtered by date:
|
|||
|
|
|
|||
|
|
| Entity | Filter field | Default window |
|
|||
|
|
|---|---|---|
|
|||
|
|
| Tickets | `createDate` | Last 2 years |
|
|||
|
|
| Tasks | `createDate` | Last 2 years |
|
|||
|
|
| Time Entries | `dateWorked` | Last 2 years |
|
|||
|
|
| Billing Items | `itemDate` | Last 2 years |
|
|||
|
|
|
|||
|
|
The `yearsBack` parameter (default `2`) controls how far back these go. The full sync and chunked sync accept a custom `yearsBack` value.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Real-Time Webhooks
|
|||
|
|
|
|||
|
|
Autotask pushes change events to `/api/webhooks/autotask` immediately when a ticket (or other entity) is created or updated. The webhook handler fetches the full record from the Autotask API and upserts it directly — no waiting for the next scheduled sync.
|
|||
|
|
|
|||
|
|
This means tickets assigned, updated, or closed in Autotask appear in Pulse within seconds. The webhook path **bypasses** the resource ID validation step, so it always writes exactly what Autotask sends.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Manual / API-Triggered Syncs
|
|||
|
|
|
|||
|
|
### Via Admin UI
|
|||
|
|
|
|||
|
|
**Admin → Sync** — the sync overview page has buttons to trigger syncs per integration.
|
|||
|
|
|
|||
|
|
### Via API endpoints
|
|||
|
|
|
|||
|
|
All endpoints are POST and non-blocking (fire-and-forget) — they return immediately and run in the background.
|
|||
|
|
|
|||
|
|
| Endpoint | Payload | Description |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `POST /api/sync/full` | `{ yearsBack?, triggeredBy? }` | Full sync of all entities |
|
|||
|
|
| `POST /api/sync/entity` | `{ entities: [...], syncType?, yearsBack? }` | Sync specific entity types only |
|
|||
|
|
| `POST /api/sync/tickets-chunked` | `{ yearsBack? }` | Chunked ticket sync with progress logging (good for large date ranges) |
|
|||
|
|
|
|||
|
|
**Example — sync only tickets and resources:**
|
|||
|
|
```bash
|
|||
|
|
curl -X POST http://localhost:3100/api/sync/entity \
|
|||
|
|
-H "Content-Type: application/json" \
|
|||
|
|
-d '{"entities": ["tickets", "resources"], "triggeredBy": "manual"}'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Example — full sync going back 3 years:**
|
|||
|
|
```bash
|
|||
|
|
curl -X POST http://localhost:3100/api/sync/full \
|
|||
|
|
-H "Content-Type: application/json" \
|
|||
|
|
-d '{"yearsBack": 3, "triggeredBy": "manual"}'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Via OpenClaw (external agent API)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Incremental sync
|
|||
|
|
POST /api/openclaw/sync/autotask/incremental
|
|||
|
|
|
|||
|
|
# Full sync
|
|||
|
|
POST /api/openclaw/sync/autotask/full { "yearsBack": 2 }
|
|||
|
|
|
|||
|
|
# Specific entities
|
|||
|
|
POST /api/openclaw/sync/autotask/entity { "entities": ["tickets"] }
|
|||
|
|
```
|
|||
|
|
Auth: `x-openclaw-key: <OPENCLAW_API_KEY>`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Valid Entity Names
|
|||
|
|
|
|||
|
|
Use these string values in the `entities` array:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
companies, resources, statuses, issue_types, sub_issue_types, work_types,
|
|||
|
|
queues, priorities, ticket_categories, contacts, projects, tickets, tasks,
|
|||
|
|
configuration_items, contracts, contract_services, autotask_services,
|
|||
|
|
billing_items, time_entries, ticket_notes, tag_groups, tags
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Sync History & Status
|
|||
|
|
|
|||
|
|
Every sync run writes to `sync_history`. You can query it directly:
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- Last sync per entity type
|
|||
|
|
SELECT entity_type, sync_type, status, records_added, records_updated, completed_at
|
|||
|
|
FROM sync_history
|
|||
|
|
WHERE status = 'completed'
|
|||
|
|
ORDER BY completed_at DESC;
|
|||
|
|
|
|||
|
|
-- Check when tickets last synced successfully
|
|||
|
|
SELECT completed_at FROM sync_history
|
|||
|
|
WHERE entity_type = 'tickets' AND status = 'completed'
|
|||
|
|
ORDER BY completed_at DESC LIMIT 1;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The incremental sync uses `completed_at` from `sync_history` to determine how far back to pull. **If a sync fails, the timestamp does not advance** — the next run re-fetches from the last successful point.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Known Constraints & Gotchas
|
|||
|
|
|
|||
|
|
- **One sync at a time.** If a sync is already running, new requests return `409 Conflict`.
|
|||
|
|
- **`assigned_resource_id` is the only resource assignment on a ticket.** There is no separate "primary resource" — `assigned_resource_id` is it. Other resource fields (`first_response_assigned_resource_id`, `last_activity_resource_id`, `creator_resource_id`) are audit/SLA tracking fields.
|
|||
|
|
- **BIGINT IDs come back as strings from pg.** The sync code uses `Number()` coercion when building validation sets to avoid false-negative ID comparisons.
|
|||
|
|
- **FK constraints on tickets are deferrable.** `tickets_company_id_fkey` and all resource FKs are `DEFERRABLE INITIALLY DEFERRED ON DELETE SET NULL`. This means a ticket can be inserted even if its referenced company or resource isn't in the local DB yet — the field is set to NULL rather than rejecting the row.
|
|||
|
|
- **Webhook and scheduled sync can race.** If a webhook fires during a scheduled sync batch, it may be overwritten by the batch. This is harmless — both write the same Autotask data.
|