# 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=` **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 `< *"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}/?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}/?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}//${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}//${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