# Opportunities

Manage sales opportunities through your pipeline. Canonical path is /opportunities; the legacy /deals path remains supported. Sub-resources cover products, product costs, contacts, users, activities, tasks, work orders, pipeline moves, file/document/image/spreadsheet attachments, and invoices.

**Base URL:** `https://api.trustpager.com/functions/v1/api/v1`

## Endpoints

### GET /opportunities

List all opportunities with cursor-based pagination. Supports search, status, contact, company, pipeline, stage, and date filters. Always includes pipeline placements. Response always includes read-only referral attribution fields: primary_referrer_contact_id (UUID of the most-recent referrer, maintained by a Postgres trigger) and primary_referrer_category (workspace category string). Use expand=referrer to inline the full referrer contact object. When using expand=products, payment_status on each product is stripped unless the caller has invoices:read scope. Legacy alias: GET /deals (same response shape).

**Scopes:** `opportunities:read` — [full detail](./opportunities/get-opportunities.md)

### GET /opportunities/:id

Retrieve a single opportunity by ID with pipeline placements. Supports expansions. Legacy alias: GET /deals/:id.

**Scopes:** `opportunities:read` — [full detail](./opportunities/get-opportunities-id.md)

### POST /opportunities

Create a new opportunity. name is required. Set skip_automations: true to suppress the deal_created trigger -- recommended for historical imports to avoid spamming contacts with automation emails. Legacy alias: POST /deals.

**Scopes:** `opportunities:write` — [full detail](./opportunities/post-opportunities.md)

### PATCH /opportunities/:id

Update an existing opportunity. Only include fields you want to change. Every successful PATCH emits a field-level audit row to crm_field_change_log (viewable at /data/crm-logs with the crm_audit:read scope). Legacy alias: PATCH /deals/:id.

**Scopes:** `opportunities:write` — [full detail](./opportunities/patch-opportunities-id.md)

### DELETE /opportunities/:id

Delete an opportunity. Legacy alias: DELETE /deals/:id.

**Scopes:** `opportunities:write` — [full detail](./opportunities/delete-opportunities-id.md)

### POST /opportunities/search

Search opportunities by name, contact name/email/phone, or company name/email/phone. Matches across opportunity name and linked contact/company fields. Legacy alias: POST /deals/search.

**Scopes:** `opportunities:read` — [full detail](./opportunities/post-opportunities-search.md)

### POST /opportunities/:id/move

Move an opportunity to a pipeline stage. If the opportunity is not in the pipeline, it will be added. If it is in a different pipeline, it will be moved. When the stage actually changes, stage_changed automations fire automatically. Use skip_automations=true to suppress all automation triggers, or pass skip_action_ids with an array of action UUIDs to suppress only specific actions within automations (the automation still runs and is logged, but those actions are bypassed and recorded in the run's skipped_action_ids field). Legacy alias: POST /deals/:id/move.

**Scopes:** `opportunities:write` — [full detail](./opportunities/post-opportunities-id-move.md)

### POST /opportunities/:id/add-card

Add an opportunity card to a pipeline WITHOUT removing existing cards in other pipelines. Enables an opportunity to appear in multiple pipelines simultaneously (dual/multi placement). If the opportunity already has a card in the target pipeline, its stage is updated. Stage_changed automations fire on new placements or stage changes. Supports skip_automations and skip_action_ids for fine-grained automation control. Legacy alias: POST /deals/:id/add-card.

**Scopes:** `opportunities:write` — [full detail](./opportunities/post-opportunities-id-add-card.md)

### GET /opportunities/:id/products

List products attached to an opportunity with pricing and payment status. payment_status is only included in the response when the caller has invoices:read scope. Legacy alias: GET /deals/:id/products.

**Scopes:** `opportunities:read` — [full detail](./opportunities/get-opportunities-id-products.md)

### POST /opportunities/:id/products

Add a product to an opportunity. product_id is required. Optionally set payment_status (requires invoices:write scope). Valid payment_status values: unpaid (default), deposit_invoiced, invoiced, paid. Legacy alias: POST /deals/:id/products.

**Scopes:** `opportunities:write` — [full detail](./opportunities/post-opportunities-id-products.md)

### PATCH /opportunities/:id/products/:opportunityProductId

Update an opportunity product. Supports quantity, price, discount, deposit, and payment_status. Setting payment_status requires invoices:write scope -- returns 403 otherwise. Valid payment_status values: unpaid, deposit_invoiced, invoiced, paid. Legacy alias: PATCH /deals/:id/products/:dealProductId.

**Scopes:** `opportunities:write` — [full detail](./opportunities/patch-opportunities-id-products-opportunityProductId.md)

### DELETE /opportunities/:id/products/:opportunityProductId

Remove a product from an opportunity. Legacy alias: DELETE /deals/:id/products/:dealProductId.

**Scopes:** `opportunities:write` — [full detail](./opportunities/delete-opportunities-id-products-opportunityProductId.md)

### POST /opportunities/:id/products/reorder

Reorder products on an opportunity by providing an ordered array of opportunity product IDs. Legacy alias: POST /deals/:id/products/reorder.

**Scopes:** `opportunities:write` — [full detail](./opportunities/post-opportunities-id-products-reorder.md)

### GET /opportunities/:id/product-costs/:opportunityProductId

List costs for an opportunity product. Legacy alias: GET /deals/:id/product-costs/:dealProductId.

**Scopes:** `opportunities:read` — [full detail](./opportunities/get-opportunities-id-product-costs-opportunityProductId.md)

### POST /opportunities/:id/product-costs/:opportunityProductId

Create a cost entry for an opportunity product. Legacy alias: POST /deals/:id/product-costs/:dealProductId.

**Scopes:** `opportunities:write` — [full detail](./opportunities/post-opportunities-id-product-costs-opportunityProductId.md)

### PATCH /opportunities/:id/product-costs/:costId

Update an opportunity product cost entry. Legacy alias: PATCH /deals/:id/product-costs/:costId.

**Scopes:** `opportunities:write` — [full detail](./opportunities/patch-opportunities-id-product-costs-costId.md)

### DELETE /opportunities/:id/product-costs/:costId

Delete an opportunity product cost entry. Legacy alias: DELETE /deals/:id/product-costs/:costId.

**Scopes:** `opportunities:write` — [full detail](./opportunities/delete-opportunities-id-product-costs-costId.md)

### GET /opportunities/:id/contacts

List all contacts associated with an opportunity (beyond the primary contact). Legacy alias: GET /deals/:id/contacts.

**Scopes:** `opportunities:read`, `contacts:read` — [full detail](./opportunities/get-opportunities-id-contacts.md)

### POST /opportunities/:id/contacts/:contactId

Associate an additional contact with an opportunity. Legacy alias: POST /deals/:id/contacts/:contactId.

**Scopes:** `opportunities:write` — [full detail](./opportunities/post-opportunities-id-contacts-contactId.md)

### DELETE /opportunities/:id/contacts/:contactId

Remove a contact association from an opportunity. Legacy alias: DELETE /deals/:id/contacts/:contactId.

**Scopes:** `opportunities:write` — [full detail](./opportunities/delete-opportunities-id-contacts-contactId.md)

### GET /opportunities/:id/users

List users assigned to an opportunity. Legacy alias: GET /deals/:id/users.

**Scopes:** `opportunities:read` — [full detail](./opportunities/get-opportunities-id-users.md)

### POST /opportunities/:id/users/:userId

Assign a user to an opportunity. Legacy alias: POST /deals/:id/users/:userId.

**Scopes:** `opportunities:write` — [full detail](./opportunities/post-opportunities-id-users-userId.md)

### DELETE /opportunities/:id/users/:userId

Remove a user assignment from an opportunity. Legacy alias: DELETE /deals/:id/users/:userId.

**Scopes:** `opportunities:write` — [full detail](./opportunities/delete-opportunities-id-users-userId.md)

### GET /opportunities/:id/activities

List all activities for an opportunity. Legacy alias: GET /deals/:id/activities.

**Scopes:** `opportunities:read`, `activities:read` — [full detail](./opportunities/get-opportunities-id-activities.md)

### GET /opportunities/:id/tasks

List all tasks for an opportunity. Legacy alias: GET /deals/:id/tasks.

**Scopes:** `opportunities:read`, `tasks:read` — [full detail](./opportunities/get-opportunities-id-tasks.md)

### GET /opportunities/:id/work-orders

List all work orders for an opportunity. Legacy alias: GET /deals/:id/work-orders.

**Scopes:** `opportunities:read` — [full detail](./opportunities/get-opportunities-id-work-orders.md)

### GET /opportunities/:id/files

List files attached to an opportunity. Returns file metadata for every file linked to the opportunity (any file type accepted by /files -- documents, images, secure). Legacy alias: GET /deals/:id/files. Matching MCP tool: list_opportunity_files.

**Scopes:** `opportunities:read`, `files:read` — [full detail](./opportunities/get-opportunities-id-files.md)

### POST /opportunities/:id/files/:fileId

Attach an existing file to an opportunity. The file must already exist in the workspace (upload via POST /files first if needed). Legacy alias: POST /deals/:id/files/:fileId. Matching MCP tool: add_opportunity_file.

**Scopes:** `opportunities:write`, `files:read` — [full detail](./opportunities/post-opportunities-id-files-fileId.md)

### DELETE /opportunities/:id/files/:fileId

Remove a file attachment from an opportunity. Detaches the link only -- the file itself is not deleted (use DELETE /files/:id to fully delete the file). Legacy alias: DELETE /deals/:id/files/:fileId. Matching MCP tool: remove_opportunity_file.

**Scopes:** `opportunities:write` — [full detail](./opportunities/delete-opportunities-id-files-fileId.md)

### GET /opportunities/:id/documents

List CRM documents attached to an opportunity. Returns generated documents (proposals, contracts, etc.) linked to the opportunity. Legacy alias: GET /deals/:id/documents. Matching MCP tool: list_opportunity_documents.

**Scopes:** `opportunities:read`, `documents:read` — [full detail](./opportunities/get-opportunities-id-documents.md)

### POST /opportunities/:id/documents/:documentId

Attach an existing CRM document to an opportunity. Legacy alias: POST /deals/:id/documents/:documentId. Matching MCP tool: add_opportunity_document.

**Scopes:** `opportunities:write`, `documents:read` — [full detail](./opportunities/post-opportunities-id-documents-documentId.md)

### DELETE /opportunities/:id/documents/:documentId

Remove a document attachment from an opportunity. Detaches the link only -- the document itself is not deleted. Legacy alias: DELETE /deals/:id/documents/:documentId. Matching MCP tool: remove_opportunity_document.

**Scopes:** `opportunities:write` — [full detail](./opportunities/delete-opportunities-id-documents-documentId.md)

### GET /opportunities/:id/images

List image files attached to an opportunity. Filters /files attachments to type=image. Legacy alias: GET /deals/:id/images. Matching MCP tool: list_opportunity_images.

**Scopes:** `opportunities:read`, `files:read` — [full detail](./opportunities/get-opportunities-id-images.md)

### POST /opportunities/:id/images/:imageId

Attach an existing image file (type=image) to an opportunity. Legacy alias: POST /deals/:id/images/:imageId. Matching MCP tool: add_opportunity_image.

**Scopes:** `opportunities:write`, `files:read` — [full detail](./opportunities/post-opportunities-id-images-imageId.md)

### DELETE /opportunities/:id/images/:imageId

Remove an image attachment from an opportunity. Detaches the link only -- the image file itself is not deleted. Legacy alias: DELETE /deals/:id/images/:imageId. Matching MCP tool: remove_opportunity_image.

**Scopes:** `opportunities:write` — [full detail](./opportunities/delete-opportunities-id-images-imageId.md)

### GET /opportunities/:id/spreadsheets

List spreadsheets attached to an opportunity. Returns metadata for every spreadsheet linked to the opportunity (including spreadsheets auto-created by form submissions where the form is linked to the opportunity). Legacy alias: GET /deals/:id/spreadsheets. Matching MCP tool: list_opportunity_spreadsheets.

**Scopes:** `opportunities:read`, `spreadsheets:read` — [full detail](./opportunities/get-opportunities-id-spreadsheets.md)

### POST /opportunities/:id/spreadsheets/:spreadsheetId

Attach an existing spreadsheet to an opportunity. Legacy alias: POST /deals/:id/spreadsheets/:spreadsheetId. Matching MCP tool: add_opportunity_spreadsheet.

**Scopes:** `opportunities:write`, `spreadsheets:read` — [full detail](./opportunities/post-opportunities-id-spreadsheets-spreadsheetId.md)

### DELETE /opportunities/:id/spreadsheets/:spreadsheetId

Remove a spreadsheet attachment from an opportunity. Detaches the link only -- the spreadsheet itself is not deleted. Legacy alias: DELETE /deals/:id/spreadsheets/:spreadsheetId. Matching MCP tool: remove_opportunity_spreadsheet.

**Scopes:** `opportunities:write` — [full detail](./opportunities/delete-opportunities-id-spreadsheets-spreadsheetId.md)

### GET /opportunities/:id/invoices

List invoices linked to an opportunity. Invoices are read-only here -- this endpoint surfaces every invoice that references the opportunity via trustpager_deal_id, so AI agents and integrations can answer "what has this customer been billed for?" without needing the invoices:write scope. Requires the invoices:read scope. Legacy alias: GET /deals/:id/invoices. Matching MCP tool: list_opportunity_invoices.

**Scopes:** `opportunities:read`, `invoices:read` — [full detail](./opportunities/get-opportunities-id-invoices.md)

### POST /opportunities/bulk-create

Create up to 100 opportunities in a single request. Built for historical migrations and bulk data loads. Top-level pipeline_id/stage_id act as defaults inherited by each record unless overridden. Set skip_automations: true (strongly recommended for imports) to suppress deal_created triggers across all records. Returns a created array and an errors array so partial successes can be recovered from without duplicating work on retry. Legacy alias: POST /deals/bulk-create.

**Scopes:** `opportunities:write` — [full detail](./opportunities/post-opportunities-bulk-create.md)

### POST /opportunities/bulk-delete

Permanently delete up to 100 opportunities in a single request. Each opportunity is cascade-deleted including its products, pipeline placements, contacts, and users. Returns a count of deleted records and any IDs that failed. Cannot be undone. Legacy alias: POST /deals/bulk-delete.

**Scopes:** `opportunities:delete` — [full detail](./opportunities/post-opportunities-bulk-delete.md)

### POST /opportunities/bulk-move

Move up to 100 opportunities to a pipeline stage in a single request. Set skip_automations=true to suppress all stage_changed automation triggers (recommended for bulk moves to avoid flooding contacts). Alternatively, pass skip_action_ids to suppress only specific automation actions across all records in the batch. Returns a count of moved records and any IDs that failed. Legacy alias: POST /deals/bulk-move.

**Scopes:** `opportunities:write` — [full detail](./opportunities/post-opportunities-bulk-move.md)
