# Syncro API — Complete Endpoint Reference

Breadth reference for the **entire** Syncro REST API surface (~180 endpoints across 38
resource types), generated from the live OpenAPI 3.0 spec
(`https://api-docs.syncromsp.com/swagger.json`) and cross-checked against the production
tenant on **2026-07-10**. Use this to reach ANY Syncro call — not just the PSA core.

- **The high-frequency, high-risk workflows** (tickets, billing/line-items, invoices,
  estimates, schedules, appointments, customers-read) are fully documented with verified
  response shapes, gotchas, and preview gates in **`.claude/commands/syncro.md`**. Do NOT
  duplicate that logic here — this file is the map; `syncro.md` is the operating manual for
  the core, and holds the detailed asset / RMM-alert / agent-deploy workflows too.
- Everything below is reachable with the same auth + payload conventions as the core.

## Auth & conventions (same as syncro.md)

- **Base:** `https://computerguru.syncromsp.com/api/v1`
- **Auth:** `?api_key=<key>` **query param** (NOT a header). Per-user keys resolved by
  `source .claude/scripts/syncro-env.sh` → `$SYNCRO_BASE` / `$SYNCRO_API_KEY`. Never hardcode.
- **Every call is attributed to the key owner** — comments, line items, assets, invoices.
- **Payload handoff:** heredoc `--data-binary @-` with `<<'JSON'` (static) or `<<JSON`
  (interpolating `${VARS}`). Never `/tmp/*.json` on Windows.
- **Parsing:** Syncro emits unescaped control chars — `tr -d '\000-\037'` before jq, or use
  grep/sed. Never retry jq on a parse failure.
- **Rate limit:** 180 req/min per IP. 429 → wait 60s.

## Write discipline (applies to EVERY POST/PUT/PATCH/DELETE here)

1. **Preview + explicit confirmation before any write** — show the full payload.
2. **No retries on an ambiguous result** — GET the resource to confirm; Syncro has no
   idempotency, so a blind retry duplicates. Duplicates can't be deleted via API.
3. **`[TEST]` prefix** on any test article; test against ACG-internal `customer_id 15353550`.
4. **Bot alert after every successful write** (`post-bot-alert.sh`) — same as the core.
5. **DELETE is immediate and unconfirmed** on most resources (schedules, estimates, assets
   caveat below) — never probe destructive methods against live customer data.

## Live token-capability matrix (howard key, probed 2026-07-10)

| Surface | Status with current per-user token |
|---|---|
| Tickets, Customers, Invoices, Estimates, Schedules, Appointments, Line Items | **OK** (core — see syncro.md) |
| Assets: GET list/detail, **`/patches`**, **`/installed_applications`**, chat_info | **OK** (HTTP 200) |
| Assets: POST create, PUT update (name/serial/type/customer/properties) | **OK** — `name` required |
| Contacts, Contracts, Leads, Vendors, Purchase Orders, Payments, Products, Wiki, RMM Alerts | **OK** (HTTP 200) |
| `/me`, `/users`, `/settings`, `/search` | **OK** |
| **Policy Folders** (`GET/POST/PUT/DELETE /policy_folders`) | **401 — token lacks scope** |
| **Asset move via `PUT /customer_assets/{id}` `policy_folder_id`** | **needs scope** (see below) |

### Policy-folder scope (corrects the old "RMM is GUI-only" belief)

The spec is explicit on `PUT /customer_assets/{id}`:
> *"Updating only `policy_folder_id` requires **Assets - Policy Change**. Updating
> `policy_folder_id` with other asset fields requires both **Assets - Edit** and **Assets -
> Policy Change**. Nil, nonexistent, or cross-customer folder IDs return 422."*

Our per-user tokens currently **lack** the RMM/policy scope, so `/policy_folders` returns
401 and a `policy_folder_id` PUT is silently ignored (HTTP 200, no change) — exactly the
2026-06-25 symptom. This is a **token-scope gap, not an API limitation.** To enable
policy-folder move + policy CRUD: in Syncro **Admin → API Tokens**, edit the token (or issue
a Custom token) to add **Assets - Policy Change** and policy-folder read/manage permissions,
re-vault it, then re-verify with a flip-and-restore on ACG-internal asset `12335235`
(currently folder `692253`). Supersedes memory `reference-syncro-rmm-api-gui-only`.

## RMM-relevant reads (asset intelligence)

- `GET /customer_assets/{id}/patches` → `{available_patches, available_patches_meta,
  installed_patches}` — Windows patch posture per machine.
- `GET /customer_assets/{id}/installed_applications` → `{installed_applications, meta}`
  (paginated) — software inventory; use to **verify an agent/app install** after a push.
- `GET /rmm_alerts` → `{rmm_alerts, meta}` — live RMM alert feed; POST create, `{id}/mute`,
  DELETE clears.

---

# Calling any endpoint — worked templates

Every endpoint below uses the SAME auth + payload pattern (the Swagger UI "code examples" are
generated from these same schemas). Copy a template, swap the path + body fields from the
inventory. `${BASE}`/`${API_KEY}` come from `source .claude/scripts/syncro-env.sh`.

```bash
# READ (GET) — list or single. Add filters as query params (e.g. customer_id, query, per_page).
curl -s "${BASE}/<resource>?customer_id=${CUST_ID}&per_page=100&api_key=${API_KEY}" | tr -d '\000-\037' | jq '.'

# CREATE (POST) — body fields from the inventory's _body:_ line; _required:_ must be present.
curl -s -X POST "${BASE}/<resource>?api_key=${API_KEY}" -H "Content-Type: application/json" \
  --data-binary @- <<'JSON'
{ "field": "value" }
JSON

# UPDATE (PUT/PATCH) — same shape; resend any field the endpoint marks required.
curl -s -X PUT "${BASE}/<resource>/${ID}?api_key=${API_KEY}" -H "Content-Type: application/json" \
  --data-binary @- <<'JSON'
{ "field": "new value" }
JSON

# DELETE — immediate, no confirmation on most resources. Preview + confirm first.
curl -s -X DELETE "${BASE}/<resource>/${ID}?api_key=${API_KEY}"
```

Build bodies with `jq -nc --arg`/`--argjson` when interpolating shell values (JSON-safe). For
any WRITE: preview the payload, confirm, then **GET the resource back to verify** (Syncro has
no idempotency; a blind retry duplicates). Scope-gated writes can return 200 yet no-op — always
verify the value actually changed (see the policy-folder note above).

---

# Full endpoint inventory (by resource)

Legend: methods are literal. `_required:_` lists must-send body fields; `_body:_` lists all
accepted body fields (from the live schema). Absence of a body line = no JSON body / path-or-
query only. `{...}` segments are path params.

## Appointment
- `GET /appointments` — Returns a paginated list of Appointments
- `POST /appointments` — Creates an Appointment  
  _required:_ `summary,start_at`  
  _body:_ all_day, appointment_duration, appointment_type_id, customer_id, description, do_not_email, email_customer, end_at, location, start_at, summary, ticket_id, user_id, user_ids
- `DELETE /appointments/{id}` — Deletes an Appointment by ID
- `GET /appointments/{id}` — Retrieves an Appointment by ID
- `PUT /appointments/{id}` — Updates an existing Appointment by ID  
  _required:_ `start_at`  
  _body:_ all_day, appointment_duration, appointment_type_id, customer_id, description, email_customer, end_at, location, start_at, summary, ticket_id, user_id, user_ids

## Appointment Type
- `GET /appointment_types` — Returns a paginated list of Appointment Types
- `POST /appointment_types` — Creates an Appointment Type  
  _required:_ `name`  
  _body:_ email_instructions, location_hard_code, location_type, name
- `DELETE /appointment_types/{id}` — Deletes an Appointment Type by ID
- `GET /appointment_types/{id}` — Retrieves an Appointment Type by ID
- `PUT /appointment_types/{id}` — Updates an existing Appointment Type by ID  
  _body:_ email_instructions, location_hard_code, location_type, name

## Asset
- `GET /customer_assets` — Returns a paginated list of Assets
- `POST /customer_assets` — Creates an Asset  
  _required:_ `name`  
  _body:_ asset_serial, asset_type_id, asset_type_name, customer_id, name, properties
- `GET /customer_assets/chat_information_by_ids` — Retrieves Assets chat informations by IDs
- `GET /customer_assets/{id}` — Retrieves an Asset by ID
- `PUT /customer_assets/{id}` — Updates an existing Asset by ID  
  _required:_ `name`  
  _body:_ asset_serial, asset_type_id, asset_type_name, customer_id, name, policy_folder_id, properties
- `GET /customer_assets/{id}/installed_applications` — Retrieves installed applications for an Asset
- `GET /customer_assets/{id}/patches` — Retrieves Windows patch data for an Asset

## Call
- `GET /callerid` — Get Caller ID

## Canned Response
- `GET /canned_responses` — Returns a list of Canned Responses with a query
- `POST /canned_responses` — Creates a new Canned Response  
  _required:_ `title,body`  
  _body:_ body, canned_response_category_id, subject, title
- `GET /canned_responses/settings` — Returns the settings for Canned Responses
- `DELETE /canned_responses/{id}` — Deletes a Canned Response
- `PATCH /canned_responses/{id}` — Updates a Canned Response  
  _body:_ body, canned_response_category_id, subject, title

## Contact
- `GET /contacts` — Returns a paginated list of Contacts
- `POST /contacts` — Creates a Contact  
  _required:_ `customer_id`  
  _body:_ address1, address2, city, customer_id, email, enable_portal_user, extension, mobile, name, notes, phone, state, zip
- `DELETE /contacts/{id}` — Deletes a Contact
- `GET /contacts/{id}` — Retrieves a Contact by ID
- `PUT /contacts/{id}` — Updates an existing Contact  
  _required:_ `name`  
  _body:_ address1, address2, city, customer_id, email, enable_portal_user, extension, mobile, name, notes, phone, state, title, zip

## Contract
- `GET /contracts` — Returns a paginated list of Contracts
- `POST /contracts` — Creates a Contract  
  _required:_ `customer_id`  
  _body:_ apply_to_all, contract_amount, customer_id, description, end_date, likelihood, name, primary_contact, sla_id, start_date, status
- `DELETE /contracts/{id}` — Deletes a Contract by ID
- `GET /contracts/{id}` — Retrieves a Contract by ID
- `PUT /contracts/{id}` — Updates an existing Contract by ID  
  _required:_ `customer_id`  
  _body:_ apply_to_all, contract_amount, customer_id, description, end_date, likelihood, name, primary_contact, sla_id, start_date, status

## Customer
- `GET /customers` — Returns a paginated list of customers
- `POST /customers` — Creates a Customer
- `GET /customers/autocomplete` — Returns a paginated list of customers for autocomplete query
- `GET /customers/latest` — Returns latest Customer
- `DELETE /customers/{id}` — Deletes a Customer by ID
- `GET /customers/{id}` — Retrieves a Customer by ID
- `PUT /customers/{id}` — Updates an existing Customer by ID

## Estimate
- `GET /estimates` — Returns a paginated list of Estimates
- `POST /estimates` — Creates an Estimate  
  _body:_ created_at, customer_id, date, line_items, location_id, name, note, number, status, ticket_id, updated_at
- `DELETE /estimates/{id}` — Deletes an Estimate by ID
- `GET /estimates/{id}` — Retrieves an Estimate by ID or number
- `PUT /estimates/{id}` — Updates an existing Estimate by ID  
  _body:_ customer_id, date, location_id, name, note, number, status, ticket_id
- `POST /estimates/{id}/convert_to_invoice` — Convert an Estimate to an Invoice
- `POST /estimates/{id}/email` — Sends an Estimate to a Customer
- `POST /estimates/{id}/line_items` — Adds a Line Item to an Estimate
- `DELETE /estimates/{id}/line_items/{line_item_id}` — Deletes a Line Item
- `PUT /estimates/{id}/line_items/{line_item_id}` — Updates a Line Item
- `POST /estimates/{id}/print` — Queues a print job for an Estimate

## Invoice
- `GET /invoices` — Returns a paginated list of Invoices
- `POST /invoices` — Creates an Invoice  
  _required:_ `customer_id,number,date`  
  _body:_ balance_due, contact_id, created_at, customer_business_then_name, customer_id, date, due_date, hardwarecost, id, is_paid, line_items, location_id, note, number, pdf_url, po_number, subtotal, tax, tech_marked_paid, ticket_id, total, updated_at, verified_paid
- `DELETE /invoices/{id}` — Deletes an invoice by ID
- `GET /invoices/{id}` — Retrieves an Invoice by ID or Number
- `PUT /invoices/{id}` — Updates an existing invoice by ID  
  _body:_ contact_id, created_at, customer_business_then_name, customer_id, date, due_date, hardwarecost, location_id, note, number, pdf_url, po_number, subtotal, tax, ticket_id, total, updated_at
- `POST /invoices/{id}/email` — Sends invoice to customer
- `POST /invoices/{id}/print` — Queues a print job for an invoice
- `GET /invoices/{id}/ticket` — Returns the associated ticket for an invoice

## Invoice/Line item
- `POST /invoices/{id}/line_items` — Creates a new line item
- `DELETE /invoices/{id}/line_items/{line_item_id}` — Deletes an a line item of an invoice by ID
- `PUT /invoices/{id}/line_items/{line_item_id}` — Updates an a line item of an invoice by ID

## Item
- `GET /items` — Returns a paginated list of Part Orders

## Lead
- `GET /leads` — Returns a paginated list of Leads
- `POST /leads` — Creates a Lead  
  _body:_ address, appointment_time, appointment_type_id, business_name, city, contact_id, converted, customer_id, customer_purchase_id, disabled, email, first_name, from_check_in, hidden_notes, last_name, likelihood, location_id, mailbox_id, message_read, mobile, opportunity_amount_dollars, opportunity_start_date, phone, properties, signature_data, signature_date, signature_name, state, status, ticket_description, ticket_id, ticket_problem_type, ticket_properties, ticket_subject, ticket_type_id, user_id, zip
- `GET /leads/{id}` — Retrieves a Lead by ID
- `PUT /leads/{id}` — Updates an existing Lead by ID  
  _body:_ address, appointment_time, appointment_type_id, business_name, city, contact_id, converted, customer_id, customer_purchase_id, disabled, email, first_name, from_check_in, hidden_notes, last_name, likelihood, location_id, mailbox_id, message_read, mobile, opportunity_amount_dollars, opportunity_start_date, phone, properties, signature_data, signature_date, signature_name, state, status, ticket_description, ticket_id, ticket_problem_type, ticket_properties, ticket_subject, ticket_type_id, user_id, zip

## Line Item
- `GET /line_items` — Returns a paginated list of Line Items

## New Ticket Form
- `GET /new_ticket_forms` — Returns a paginated list of Ticket Forms
- `GET /new_ticket_forms/{id}` — Retrieves a Ticket Form
- `POST /new_ticket_forms/{id}/process_form` — Creates a new Ticket for a Ticket Form  
  _body:_ appointments, customer_details, ticket_details

## Payment
- `GET /payments` — Returns a paginated list of Payments
- `POST /payments` — Creates a Payment  
  _body:_ address_city, address_street, address_zip, amount_cents, apply_payments, credit_card_number, customer_id, cvv, date_month, date_year, firstname, invoice_id, invoice_number, lastname, payment_method, ref_num, register_id, signature_data, signature_date, signature_name
- `GET /payments/{id}` — Retrieves a Payment by ID

## Payment Method
- `GET /payment_methods` — Returns a paginated list of Payment Methods

## Payment Profile
- `GET /customers/{customer_id}/payment_profiles` — Returns a paginated list of Payment Profiles
- `POST /customers/{customer_id}/payment_profiles` — Creates a Payment Profile  
  _body:_ customer_external_id, expiration, last_four, payment_profile_id
- `DELETE /customers/{customer_id}/payment_profiles/{id}` — Deletes a Payment Profile
- `GET /customers/{customer_id}/payment_profiles/{id}` — Retrieves a Payment Profile by ID
- `PUT /customers/{customer_id}/payment_profiles/{id}` — Updates a Payment Profile  
  _body:_ expiration, last_four

## Phone
- `GET /customers/{customer_id}/phones` — Returns a paginated list of Phones
- `POST /customers/{customer_id}/phones` — Creates a Phone
- `DELETE /customers/{customer_id}/phones/{id}` — Deletes a Phone by ID
- `PUT /customers/{customer_id}/phones/{id}` — Updates an existing Phone by ID

## Policy Folder
- `GET /policy_folders` — Returns a paginated list of Policy Folders
- `POST /policy_folders` — Creates a Policy Folder  
  _required:_ `customer_id,name,parent_id`  
  _body:_ customer_id, name, parent_id
- `DELETE /policy_folders/{id}` — Deletes a Policy Folder by ID
- `GET /policy_folders/{id}` — Retrieves a Policy Folder by ID
- `PUT /policy_folders/{id}` — Updates an existing Policy Folder by ID  
  _body:_ name, parent_id, partial_policy_id

## Portal User
- `GET /portal_users` — Returns a paginated list of Portal Users
- `POST /portal_users` — Creates a Portal User
- `POST /portal_users/create_invitation` — Creates an Invitation for a Portal User  
  _body:_ id
- `DELETE /portal_users/{id}` — Deletes a Portal User by ID
- `PUT /portal_users/{id}` — Updates an existing Portal User by ID

## Product
- `GET /products` — Returns a paginated list of Products
- `POST /products` — Creates a Product  
  _required:_ `name,description`  
  _body:_ category_ids, condition, description, desired_stock_level, disabled, discount_percent, maintain_stock, name, notes, physical_location, price_cost, price_retail, price_wholesale, product_category, product_skus_attributes, qb_item_id, quantity, reorder_at, serialized, sort_order, tax_rate_id, taxable, upc_code, vendor_ids, warranty, warranty_template_id
- `GET /products/barcode` — Returns a Product by Barcode
- `GET /products/categories` — Returns a paginated list of Product Categories
- `GET /products/{id}` — Retrieves a Product by ID
- `PUT /products/{id}` — Updates an existing Product by ID  
  _required:_ `name,description`  
  _body:_ category_ids, condition, description, desired_stock_level, disabled, discount_percent, maintain_stock, name, notes, physical_location, price_cost, price_retail, price_wholesale, product_category, product_skus_attributes, qb_item_id, quantity, reorder_at, serialized, sort_order, tax_rate_id, taxable, upc_code, vendor_ids, warranty, warranty_template_id
- `POST /products/{id}/add_images` — Creates a Product Image
- `DELETE /products/{id}/delete_image` — Deletes a Product Image
- `PUT /products/{id}/location_quantities` — Updates a Location Quantity  
  _body:_ location_quantity_id, quantity

## Product Serial
- `GET /products/{product_id}/product_serials` — Returns a paginated list of Product_serials
- `POST /products/{product_id}/product_serials` — Creates a Product Serial  
  _body:_ condition, price_cost_cents, price_retail_cents, serial_number
- `POST /products/{product_id}/product_serials/attach_to_line_item` — Adds Product Serials to a Line Item  
  _body:_ line_item_id, product_serial_ids, record_type
- `PUT /products/{product_id}/product_serials/{id}` — Updates an existing Product Serial by ID  
  _body:_ condition, notes, price_cost_cents, price_retail_cents, serial_number

## Product Sku
- `GET /products/{product_id}/product_skus` — Returns list of Product Skus
- `POST /products/{product_id}/product_skus` — Creates a Product Sku  
  _body:_ value, vendor_id
- `PUT /products/{product_id}/product_skus/{id}` — Updates an existing Product Sku by ID  
  _body:_ value, vendor_id

## Purchase Order
- `GET /purchase_orders` — Returns a paginated list of Purchase Orders
- `POST /purchase_orders` — Creates a Purchase Order  
  _body:_ delivery_tracking, discount_percent, due_date, expected_date, general_notes, location_id, order_date, other_cents, paid_date, shipping_cents, shipping_notes, user_id, vendor_id
- `GET /purchase_orders/{id}` — Retrieves a Purchase Order by ID
- `POST /purchase_orders/{id}/create_po_line_item` — Adds a Product to a Purchase Order  
  _body:_ product_id, quantity
- `POST /purchase_orders/{id}/receive` — receive purchase_order  
  _body:_ line_item_id

## RMM Alert
- `GET /rmm_alerts` — Returns a paginated list of RMM Alerts
- `POST /rmm_alerts` — Creates an RMM Alert  
  _body:_ rmm_alert
- `DELETE /rmm_alerts/{id}` — Deletes/Clears an RMM Alert by ID
- `GET /rmm_alerts/{id}` — Retrieves an RMM Alert by ID
- `POST /rmm_alerts/{id}/mute` — Mutes an RMM Alert by ID

## Schedule
- `GET /schedules` — Returns a paginated list of Invoice Schedules
- `POST /schedules` — Creates an Invoice Schedule
- `DELETE /schedules/{id}` — Deletes a Schedule by ID
- `GET /schedules/{id}` — Retrieves a Schedule by ID
- `PUT /schedules/{id}` — Updates an existing Invoice Schedule by ID
- `POST /schedules/{id}/add_line_item` — Adds a Line Item to an Invoice Schedule
- `PUT /schedules/{id}/line_items/{schedule_line_item_id}` — Updates a Line Item
- `POST /schedules/{id}/remove_line_item` — Removes a Line Item from an Invoice Schedule

## Search
- `GET /search` — Search all the things

## Setting
- `GET /settings` — Returns a list of Account Settings
- `GET /settings/printing` — Returns Printing Settings
- `GET /settings/tabs` — Returns Tabs Settings

## Ticket
- `GET /ticket_comments` — Returns a paginated flat list of comments across multiple tickets
- `GET /tickets` — Returns a paginated list of Tickets
- `POST /tickets` — Creates a Ticket
- `GET /tickets/settings` — Returns Tickets Settings
- `DELETE /tickets/{id}` — Deletes a Ticket by ID
- `GET /tickets/{id}` — Retrieves a Ticket by ID
- `PUT /tickets/{id}` — Updates an existing Ticket by ID
- `POST /tickets/{id}/add_line_item` — Creates a Ticket Line Item  
  _body:_ description, name, price_cost, price_retail, product_id, quantity, taxable, upc_code
- `POST /tickets/{id}/attach_file_url` — Attach a file to a Ticket
- `POST /tickets/{id}/charge_timer_entry` — Charges a Ticket Timer
- `POST /tickets/{id}/comment` — Adds a Comment to a Ticket  
  _body:_ body, do_not_email, hidden, sms_body, subject, tech
- `GET /tickets/{id}/comments` — Returns Comments for a Ticket
- `POST /tickets/{id}/delete_attachment` — Deletes a Ticket Attachment  
  _body:_ attachment_id
- `POST /tickets/{id}/delete_timer_entry` — Deletes a Ticket Timer
- `POST /tickets/{id}/print` — Prints a Ticket by ID
- `POST /tickets/{id}/remove_line_item` — Deletes a Ticket Line Item  
  _body:_ ticket_line_item_id
- `POST /tickets/{id}/timer_entry` — Create a Ticket Timer for a Ticket  
  _body:_ duration_minutes, end_at, notes, product_id, start_at, user_id
- `PUT /tickets/{id}/update_line_item` — Updates an existing Ticket Line Item  
  _body:_ description, name, price_cost, price_retail, product_id, quantity, taxable, ticket_line_item_id, upc_code
- `PUT /tickets/{id}/update_timer_entry` — Updates an existing Ticket Timer  
  _body:_ duration_minutes, notes, product_id, start_at, timer_entry_id, user_id

## Ticket Blueprint
- `GET /ticket_blueprints` — Returns a paginated list of Ticket Blueprints
- `POST /ticket_blueprints/{id}/apply` — Creates tickets from a Blueprint  
  _required:_ `customer_id`  
  _body:_ customer_id

## Ticket Timer
- `GET /ticket_timers` — Returns a paginated list of Ticket Timers
- `PATCH ticket_timers/{id}` — Update the billable property of a Ticket Timer

## Timelog
- `GET /timelogs` — Returns a paginated list of Timelogs
- `PUT /timelogs` — Updates a Timelog  
  _body:_ in_at, in_note, lunch, out_at, out_note
- `GET /timelogs/last` — Returns last Timelog

## User
- `GET /me` — Returns the current user
- `POST /otp_login` — Authorize a User with One Time Password  
  _body:_ code
- `GET /users` — Returns a paginated list of Users
- `GET /users/{id}` — Retrieves an existing User by ID

## User Device
- `POST /user_devices` — Creates a User Device  
  _body:_ device_name, device_uuid, model, registration_token_gcm, screen_size, system_name
- `GET /user_devices/{id}` — Retrieves an existing User Device by UUID
- `PUT /user_devices/{id}` — Updates an existing User Device by UUID  
  _body:_ registration_token_gcm

## Vendor
- `GET /vendors` — Returns a paginated list of Vendors
- `POST /vendors` — Creates a Vendor
- `GET /vendors/{id}` — Retrieves a Vendor Page
- `PUT /vendors/{id}` — Updates an existing Vendor page by ID

## Wiki Page
- `GET /wiki_pages` — Returns a paginated list of Wiki Pages
- `POST /wiki_pages` — Creates a Wiki Page  
  _body:_ asset_id, body, customer_id, name, slug, visibility
- `DELETE /wiki_pages/{id}` — Deletes a Wiki Page by ID
- `GET /wiki_pages/{id}` — Retrieves a Wiki Page
- `PUT /wiki_pages/{id}` — Updates an existing Wiki Page by ID  
  _body:_ asset_id, body, customer_id, name, slug, visibility

## Worksheet Result
- `GET /tickets/{ticket_id}/worksheet_results` — Returns a paginated list of Worksheet Results
- `POST /tickets/{ticket_id}/worksheet_results` — Creates Worksheet Result  
  _body:_ title, worksheet_template_id
- `DELETE /tickets/{ticket_id}/worksheet_results/{id}` — Deletes a Worksheet Result
- `GET /tickets/{ticket_id}/worksheet_results/{id}` — Retrieves a Worksheet Result by ID
- `PUT /tickets/{ticket_id}/worksheet_results/{id}` — Updates a Worksheet Result  
  _body:_ answers, complete, public, required, title, user_id, worksheet_template_id
