# Gradient Works API Reference > Programmatic access to Gradient Works market maps, target books, distributions, retrievals, queues, reps, and carve projects. > Auth: Bearer token in `Authorization` header on all requests. ## API Reference ### CRM Connections # List crm connections `GET` `/api/v1/crm-connections` Return the CRM connections in the account linked to the API key that the key may see. Results are sorted by creation date, newest first. ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | type | string | no | Filter by CRM type: SALESFORCE or HUBSPOT | | crm_environment | string | no | Filter by environment: PRODUCTION or SANDBOX | | status | string | no | Filter by status: ACTIVE, INACTIVE, or ALL (default: ACTIVE) | | query | string | no | Case-insensitive substring search on org name or instance URL | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | Unique identifier (UUID) of the CRM connection | | type | string | CRM type: SALESFORCE or HUBSPOT | | status | string | Connection status: ACTIVE or INACTIVE | | org_name | string | CRM org display name | | instance_url | string | CRM instance URL | | crm_environment | string | Environment: PRODUCTION or SANDBOX | | is_primary | boolean | Whether this is the primary CRM connection for the account | | created_at | string | ISO-8601 creation timestamp | | updated_at | string | ISO-8601 last-updated timestamp | --- # List crm connection metadata `GET` `/api/v1/crm-connections/{crm_connection_id}/metadata` Return the field schema for the requested CRM object type: `account` (Salesforce) or `company` (HubSpot). If `type` is omitted it defaults to the appropriate type for the connection. Fields with `updateable: true` can be written to; non-updateable fields (e.g. Salesforce `Id`, HubSpot `hs_object_id`) are read-only. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | — | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | type | string | no | Object type to retrieve metadata for: `account` (Salesforce only) or `company` (HubSpot only). Defaults to `account` for Salesforce connections and `company` for HubSpot connections. | | enumerate | string | no | When true, include allowed values for picklist (Salesforce) and enumeration (HubSpot) fields in the `options` array. Defaults to false. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | label | string | Human-readable field name | | value | string | CRM API field name | | field_type | string | Field data type (e.g. string, picklist, reference, boolean) | | updateable | boolean | Whether the field can be written to | | options | array[object] | Allowed values for picklist fields | ## Errors | Status | Description | |--------|-------------| | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- ### Assets # Upload asset direct `POST` `/api/v1/assets` Upload a CSV or Excel (xlsx/xls) file as multipart/form-data. Returns an asset:// URI. For Excel files, pass ``sheet_index`` (0-indexed, default 0) to select which sheet to convert to CSV. ## Request body `multipart/form-data` | Field | Type | Required | Description | |-------|------|----------|-------------| | file | string | yes | CSV or Excel file to upload | | sheet_index | integer | no | Sheet index (0-based) to convert for Excel files | ## Response `201 Created` — `application/json` | Field | Type | Description | |-------|------|-------------| | asset_id | string | An asset:// URI referencing the uploaded file | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 422 | Unprocessable Entity | --- # Get upload url upload url `POST` `/api/v1/assets/upload-url` Returns a pre-signed S3 URL and an upload token. PUT the file bytes directly to the URL, then call complete_asset_upload with the token to create the data source. Example upload step: curl -X PUT "" --upload-file "/path/to/file.csv" For Excel files, pass sheet_index (0-based) to select which sheet to convert to CSV. ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | file_name | string | yes | File name including extension (e.g. 'accounts.csv' or 'data.xlsx') | | sheet_index | integer | no | Sheet index (0-based) to convert for Excel files | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | upload_url | string | Pre-signed S3 URL — PUT the file bytes directly to this URL | | upload_token | string | Opaque token to pass to complete_upload after the PUT succeeds | | expires_in | integer | Seconds until the upload URL and token expire | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 422 | Unprocessable Entity | --- # Complete upload complete `POST` `/api/v1/assets/complete` Finalize an upload started with upload_asset. Reads the uploaded file from S3, processes it, and creates a data source record. Returns an asset:// URI. ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | upload_token | string | yes | Token returned by upload_asset | ## Response `201 Created` — `application/json` | Field | Type | Description | |-------|------|-------------| | asset_id | string | An asset:// URI referencing the uploaded file | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 422 | Unprocessable Entity | --- ### Market Maps # List market maps `GET` `/api/v1/market-maps` Return market maps owned by the account linked to the API key. By default only ACTIVE market maps are returned; pass `?status=all` to include inactive ones. ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | status | string | no | Filter by status: 'active' (default) or 'all'. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | UUID of the market map | | name | string | Market map name | | status | string | Market map status (ACTIVE or INACTIVE) | | source_type | string | How the market map was created (e.g. csv, crm) | | analyzed_count | integer | Number of accounts analyzed (from latest completed run) | | mapped_count | integer | Number of accounts placed on the map (from latest completed run) | | tier1_count | integer | Tier 1 accounts (from latest completed run) | | latest_run_status | string | Status of the most recent run | | created_at | string | ISO-8601 creation timestamp | | updated_at | string | ISO-8601 last-updated timestamp | ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | --- # Create market map `POST` `/api/v1/market-maps` Accept configuration JSON and start an asynchronous market map creation. Returns the new market map ID with PENDING status. Poll run status via the runs endpoints. Credit/subscription validation happens server-side before accepting the request. ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | source | object | yes | Data source to create the market map from | | source.type | string | yes | Source type. Currently only 'csv' is supported. | | source.id | string | yes | asset:// URI for the data source (see Assets endpoint). | | fields | object | yes | Field mappings for the source data | | fields.id | string | yes | Column name for account ID | | fields.name | string | yes | Column name for account name | | fields.url | string | no | Column name for account website URL | | fields.target | object | yes | Target criteria: the field and values that identify reference accounts | | fields.target.field | string | yes | Column name used for target criteria | | fields.target.values | array[string] | yes | Picklist values that identify reference accounts | | fields.target_weight | string | no | Column name for prioritization weight | | discover_urls | boolean | no | Auto-discover website URLs for accounts missing a url mapping | | icp | string | no | Free-text Ideal Customer Profile description | ## Response `201 Created` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | UUID of the newly created market map | | status | string | Initial status (PENDING) | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 402 | Payment Required | | 403 | Forbidden | | 422 | Unprocessable Entity | --- # Get market map `GET` `/api/v1/market-maps/{id}` Retrieve a single market map by its ID, including the latest run status and statistics from the latest completed run. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | id | string | yes | UUID of the market map | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | UUID of the market map | | name | string | Market map name | | status | string | Market map status (ACTIVE or INACTIVE) | | source_type | string | How the market map was created (e.g. csv, crm) | | analyzed_count | integer | Number of accounts analyzed | | mapped_count | integer | Number of accounts placed on the map | | tier1_count | integer | Tier 1 accounts | | latest_run | object | The most recent run for this market map | | latest_run.id | string | UUID of the run | | latest_run.status | string | Run status: PENDING, PROCESSING, COMPLETE, FAILED, or CANCELED | | latest_run.status_message | string | Human-readable status detail | | latest_run.source_type | string | Source type (csv, salesforce_report, hubspot_list, project) | | latest_run.source_name | string | Display name of the source | | latest_run.credit_cost | integer | Research credits consumed | | latest_run.analyzed_count | integer | Total accounts analyzed (null until complete) | | latest_run.mapped_count | integer | Accounts placed on the map (null until complete) | | latest_run.tier1_count | integer | Tier 1 accounts (null until complete) | | latest_run.created_at | string | ISO-8601 creation timestamp | | latest_run.updated_at | string | ISO-8601 last-updated timestamp | | created_at | string | ISO-8601 creation timestamp | | updated_at | string | ISO-8601 last-updated timestamp | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | --- # List runs `GET` `/api/v1/market-maps/{id}/runs` Return all runs for a market map, ordered by most recent first. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | id | string | yes | UUID of the market map | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | UUID of the run | | status | string | Run status: PENDING, PROCESSING, COMPLETE, FAILED, or CANCELED | | status_message | string | Human-readable status detail | | source_type | string | Source type (csv, salesforce_report, hubspot_list, project) | | source_name | string | Display name of the source | | credit_cost | integer | Research credits consumed | | analyzed_count | integer | Total accounts analyzed (null until complete) | | mapped_count | integer | Accounts placed on the map (null until complete) | | tier1_count | integer | Tier 1 accounts (null until complete) | | created_at | string | ISO-8601 creation timestamp | | updated_at | string | ISO-8601 last-updated timestamp | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | --- # Get current run `GET` `/api/v1/market-maps/{id}/runs/current` Return the most recent run for a market map. Returns 404 if no runs exist yet. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | id | string | yes | UUID of the market map | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | UUID of the run | | status | string | Run status: PENDING, PROCESSING, COMPLETE, FAILED, or CANCELED | | status_message | string | Human-readable status detail | | source_type | string | Source type (csv, salesforce_report, hubspot_list, project) | | source_name | string | Display name of the source | | credit_cost | integer | Research credits consumed | | analyzed_count | integer | Total accounts analyzed (null until complete) | | mapped_count | integer | Accounts placed on the map (null until complete) | | tier1_count | integer | Tier 1 accounts (null until complete) | | created_at | string | ISO-8601 creation timestamp | | updated_at | string | ISO-8601 last-updated timestamp | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | --- # Get run `GET` `/api/v1/market-maps/{id}/runs/{run_id}` Retrieve a run by its ID. Returns 404 if the run does not belong to this market map. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | id | string | yes | UUID of the market map | | run_id | string | yes | UUID of the run | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | UUID of the run | | status | string | Run status: PENDING, PROCESSING, COMPLETE, FAILED, or CANCELED | | status_message | string | Human-readable status detail | | source_type | string | Source type (csv, salesforce_report, hubspot_list, project) | | source_name | string | Display name of the source | | credit_cost | integer | Research credits consumed | | analyzed_count | integer | Total accounts analyzed (null until complete) | | mapped_count | integer | Accounts placed on the map (null until complete) | | tier1_count | integer | Tier 1 accounts (null until complete) | | created_at | string | ISO-8601 creation timestamp | | updated_at | string | ISO-8601 last-updated timestamp | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | --- # Get run progress `GET` `/api/v1/market-maps/{id}/runs/{run_id}/progress` Return real-time progress for a run. For PENDING or PROCESSING runs, proxies to the Krang MK4 progress API. For terminal states, returns a synthetic progress response. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | id | string | yes | UUID of the market map | | run_id | string | yes | UUID of the run | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | status | string | Current run status | | total_count | integer | Total accounts being processed | | estimated_pct | number | Estimated progress 0–1 | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | --- # Get run results `GET` `/api/v1/market-maps/{id}/runs/{run_id}/results` Return the output artifacts of a completed run, including a pre-signed S3 download URL for the result file. Returns 409 if the run has not completed yet. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | id | string | yes | UUID of the market map | | run_id | string | yes | UUID of the run | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | download_url | string | Pre-signed S3 URL for the result file | | expires_in | integer | URL expiry in seconds | | stats | object | Result statistics | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 409 | Conflict | --- # Cancel run `POST` `/api/v1/market-maps/{id}/runs/{run_id}/cancel` Cancel a run that is PENDING or PROCESSING. Returns 409 if the run is already in a terminal state (COMPLETE, FAILED, or CANCELED). ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | id | string | yes | UUID of the market map | | run_id | string | yes | UUID of the run | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | UUID of the canceled run | | status | string | New status (CANCELED) | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 409 | Conflict | --- # List accounts `GET` `/api/v1/market-maps/{id}/accounts` Return every account with name, website, cluster assignment, score, and tier. Cluster names are resolved from the map's cluster metadata. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | id | string | yes | UUID of the market map | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | Account identifier | | name | string | Account name | | url | string | Account website URL | | cluster_id | integer | Cluster ID | | cluster_name | string | Cluster name | | score | number | Market map score | | tier | integer | Score tier (score_bin) | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | --- # Post accounts `POST` `/api/v1/market-maps/{id}/accounts` Accepts a source and field mappings. Identifies accounts from the source that are not already in the market map and adds them in a new run. Only 'csv' sources (asset:// URIs) are currently supported. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | id | string | yes | UUID of the market map | ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | source | object | yes | Data source containing accounts to add | | source.type | string | yes | Source type. Currently only 'csv' is supported. | | source.id | string | yes | asset:// URI for the data source (see Assets endpoint). | | fields | object | yes | Field mappings for the source data | | fields.id | string | yes | Column name for account ID | | fields.name | string | yes | Column name for account name | | fields.url | string | no | Column name for account website URL | ## Response `202 Accepted` — `application/json` | Field | Type | Description | |-------|------|-------------| | status | string | Run status: PENDING | | source_type | string | Source type from the request | | source_id | string | Source ID from the request | | error | string | Error message if the run could not be started | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 402 | Payment Required | | 403 | Forbidden | | 404 | Not Found | | 409 | Conflict | | 422 | Unprocessable Entity | --- # Get market map summary `GET` `/api/v1/market-maps/{id}/summary` Return top-level metrics for a market map: analyzed accounts, mapped accounts, reference accounts, total clusters, clustered accounts, tier 1 accounts, and a breakdown of accounts by tier. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | id | string | yes | UUID of the market map | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | analyzed_count | integer | Total accounts analyzed | | mapped_count | integer | Accounts placed on the map | | reference_count | integer | Reference (target) accounts | | total_clusters | integer | Number of clusters | | clustered_accounts | integer | Accounts assigned to a cluster | | tier1_count | integer | Tier 1 accounts (from DB) | | accounts_by_tier | object | Account counts keyed by tier (score_bin): {'1': N, '2': N, ...} | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | --- # List clusters `GET` `/api/v1/market-maps/{id}/clusters` Return every cluster with its name, summary, and account count. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | id | string | yes | UUID of the market map | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | cluster_id | integer | Cluster ID | | name | string | Cluster name | | summary | string | Cluster summary | | account_count | integer | Number of accounts in this cluster | | tier1_count | integer | Tier 1 accounts in this cluster | | reference_count | integer | Reference (target) accounts in this cluster | | average_score | number | Average score of accounts in this cluster | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | --- # Get cluster `GET` `/api/v1/market-maps/{id}/clusters/{cluster_id}` Return a single cluster's metadata and all its accounts. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | id | string | yes | UUID of the market map | | cluster_id | integer | yes | Cluster ID | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | cluster_id | integer | Cluster ID | | name | string | Cluster name | | summary | string | Cluster summary | | accounts | array[object] | Accounts in this cluster | | accounts.cluster_id | integer | Cluster ID that the account belongs to | | accounts.account_summary | string | Account summary | | accounts.id | string | Unique account identifier | | accounts.name | string | Account name | | accounts.score | number | Score | | accounts.score_bin | integer | Tier | | accounts.target | boolean | Whether this is a reference (target) account | | accounts.url | string | Account website URL | | accounts.x | number | X coordinate | | accounts.y | number | Y coordinate | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | --- # Export market map `GET` `/api/v1/market-maps/{id}/export` Build a CSV of all accounts in the market map, upload it to S3, and return a pre-signed download URL. Prospect accounts are limited to 100 rows. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | id | string | yes | UUID of the market map | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | download_url | string | Pre-signed S3 URL for the result file | | expires_in | integer | URL expiry in seconds | | stats | object | Result statistics | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | --- # Get account detail `GET` `/api/v1/market-maps/{id}/accounts/{account_id}` Returns summary, cluster name, score, tier, and custom attributes for a single account. Unlike the deprecated /market-maps//account/ endpoint, this includes cluster_name and a structured attributes dict instead of dynamic top-level export_field keys. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | id | string | yes | UUID of the market map | | account_id | string | yes | Account identifier | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | Unique account identifier | | name | string | Account name | | url | string | Account website URL | | account_summary | string | Account summary text | | cluster_id | integer | Cluster ID the account belongs to | | cluster_name | string | Cluster display name | | score | number | Score (null for unscored/V1 maps) | | score_bin | integer | Tier (null for unscored accounts) | | target | boolean | Whether this is a reference (target) account | | attributes | object | Custom attributes from STSV export_fields and user-mapped extra fields, as a flat key→value dict | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | --- # Get similar accounts detail `GET` `/api/v1/market-maps/{id}/accounts/{account_id}/similar` Returns reference (target) accounts nearest to the given account by map distance. Only available for accounts in Tier 1 or Tier 2. Returns 422 if the account is in Tier 3+ or unscored. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | id | string | yes | UUID of the market map | | account_id | string | yes | Account identifier | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | limit | string | no | Max similar accounts to return (default 5) | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | Unique account identifier | | name | string | Account name | | url | string | Account website URL | | cluster_id | integer | Cluster ID the account belongs to | | cluster_name | string | Cluster display name | | score | number | Score | | score_bin | integer | Tier | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | --- ### Target Books # List target books `GET` `/api/v1/crm-connections/{crm_connection_id}/target-books` Return target books for the given Salesforce CRM connection. Supports status filtering, full-text search by name or rep, sorting, and cursor-based pagination. Default page size is 50. Pass next_records_url from one response into the next request to paginate through large result sets. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | status | string | no | Filter by status: Active, Deleted, or All (default: Active) | | query | string | no | Search by target book name or active rep name | | include_segments | boolean | no | Include segment names per target book (default: true) | | include_reps | boolean | no | Include active rep names per target book (default: true) | | sort_by | string | no | Field to sort by (default: Name) | | sort_direction | string | no | Sort direction: ASC or DESC (default: ASC) | | limit | string | no | Maximum number of target books to return (default: 50). | | next_records_url | string | no | Cursor returned by a previous response to fetch the next page. When provided, all other query params are ignored. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | target_books | array[object] | List of target book records | | target_books.id | string | Salesforce ID of the target book record | | target_books.name | string | Target book name | | target_books.status | string | Status: Active or Deleted | | target_books.maximum_capacity | number | Maximum account capacity | | target_books.ownership_field | string | Salesforce ownership field API name | | target_books.created_at | string | ISO-8601 creation timestamp | | target_books.updated_at | string | ISO-8601 last-updated timestamp | | target_books.segments | array[object] | Segments (present when include_segments=true) | | target_books.reps | array[object] | Active reps (present when include_reps=true) | | next_records_url | string | Pass this value as next_records_url on the next request to fetch the following page. Null when all records have been returned. Only present when limit exceeds Salesforce's per-page maximum (2000 records). | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Create target book `POST` `/api/v1/crm-connections/{crm_connection_id}/target-books` Creates a new target book with optional segments and conditions in the linked Salesforce org. Uses the Salesforce Composite Graph API to create all records atomically. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | maximum_capacity | integer | no | — | | name | string | yes | — | | ownership_field | string | no | — | | activity_threshold_id | string | no | — | | segments | array[object] | yes | — | | segments.maximum_capacity | integer | no | — | | segments.name | string | yes | — | | segments.conditions_operator | string | yes | — | | segments.custom_condition_logic | string | no | — | | segments.conditions | array[object] | yes | — | | segments.conditions.id | string | no | Salesforce ID; present = update existing, absent = create new | | segments.conditions.field | string | yes | Salesforce field API name | | segments.conditions.operator | string | yes | Comparison operator | | segments.conditions.value | string | yes | Value to compare against | | segments.is_locked | boolean | no | — | ## Response `201 Created` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | Salesforce ID of the newly created target book | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Get target book `GET` `/api/v1/crm-connections/{crm_connection_id}/target-books/{target_book_id}` Returns the full configuration for a target book. Use include_segments, include_reps, and include_activity_threshold to control which related data is fetched (all default to true). ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | target_book_id | string | yes | Salesforce ID of the target book | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | include_segments | boolean | no | Include segments and their conditions (default: true) | | include_reps | boolean | no | Include active assigned reps (default: true) | | include_activity_threshold | boolean | no | Include the assigned activity threshold (default: true) | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | Salesforce ID of the target book record | | name | string | Target book name | | status | string | Status: Active or Deleted | | maximum_capacity | number | Maximum account capacity | | ownership_field | string | Salesforce ownership field API name | | created_at | string | ISO-8601 creation timestamp | | updated_at | string | ISO-8601 last-updated timestamp | | activity_threshold | object | Assigned activity threshold (if any) | | activity_threshold.id | string | Salesforce ID of the threshold definition | | activity_threshold.name | string | Name of the activity threshold | | segments | array[object] | Segments with their conditions (present when include_segments=true) | | reps | array[object] | Active assigned reps (present when include_reps=true) | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 502 | Bad Gateway | --- # Patch target book `PATCH` `/api/v1/crm-connections/{crm_connection_id}/target-books/{target_book_id}` All fields are optional — only provided fields are updated. Send activity_threshold_id: null to remove the threshold assignment. Returns 200 with the updated book when changes are applied, or 204 when the request results in no change (empty body, or clearing a threshold that is not set). ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | target_book_id | string | yes | Salesforce ID of the target book | ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | maximum_capacity | integer | no | — | | name | string | no | — | | ownership_field | string | no | — | | activity_threshold_id | string | no | — | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | Salesforce ID of the target book record | | name | string | Target book name | | status | string | Status: Active or Deleted | | maximum_capacity | number | Maximum account capacity | | ownership_field | string | Salesforce ownership field API name | | created_at | string | ISO-8601 creation timestamp | | updated_at | string | ISO-8601 last-updated timestamp | | activity_threshold | object | Assigned activity threshold (if any) | | activity_threshold.id | string | Salesforce ID of the threshold definition | | activity_threshold.name | string | Name of the activity threshold | | segments | array[object] | Segments with their conditions (present when include_segments=true) | | reps | array[object] | Active assigned reps (present when include_reps=true) | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Delete target book `DELETE` `/api/v1/crm-connections/{crm_connection_id}/target-books/{target_book_id}` Sets the status of the target book and any assigned reps to 'Deleted'. Triggers in the package handle downstream cleanup (queue links, distribution round robins). ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | target_book_id | string | yes | Salesforce ID of the target book | ## Response `204 No Content` — `application/json` _No response body._ ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | | 404 | Not Found | | 502 | Bad Gateway | --- # Add reps to target book `POST` `/api/v1/crm-connections/{crm_connection_id}/target-books/{target_book_id}/reps` Assigns one or more reps to the target book. Reps already on this book are skipped. Returns 409 if any rep is assigned to a different target book — no reps are added if any conflict is found. To move reps from another book use PUT instead. Returns the full list of active reps now assigned to this target book. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | target_book_id | string | yes | Salesforce ID of the target book | ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | rep_ids | array[string] | yes | Salesforce IDs of reps | ## Response `201 Created` — `application/json` | Field | Type | Description | |-------|------|-------------| | reps | array[object] | Full list of active reps now assigned to this target book | | reps.id | string | Salesforce User ID of the rep | | reps.name | string | User.Name | | reps.is_active | boolean | User.IsActive | | reps.role | object | User.UserRole, if assigned | | reps.role.id | string | — | | reps.role.name | string | — | | reps.profile | object | User.Profile, if any | | reps.profile.id | string | — | | reps.profile.name | string | — | | reps.manager | object | User.Manager, if assigned | | reps.manager.id | string | — | | reps.manager.name | string | — | | reps.target_book | object | — | | reps.target_book.id | string | — | | reps.target_book.name | string | — | | reps.metrics | object | Requested metrics keyed by metric name. Null when the caller did not request metrics. Individual values may be null for threshold-dependent metrics when the target book has no Activity Threshold configured. | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 409 | Conflict | | 502 | Bad Gateway | --- # Assign reps to target book `PUT` `/api/v1/crm-connections/{crm_connection_id}/target-books/{target_book_id}/reps` Sets the book's active rep assignments to exactly the provided list. Reps in the list who are currently on a different book are moved here. Reps currently on this book who are not in the list are removed. Reps already on this book are left unchanged. Returns the full list of active reps now assigned to this target book. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | target_book_id | string | yes | Salesforce ID of the target book | ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | rep_ids | array[string] | yes | Salesforce IDs of reps | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | reps | array[object] | Full list of active reps now assigned to this target book | | reps.id | string | Salesforce User ID of the rep | | reps.name | string | User.Name | | reps.is_active | boolean | User.IsActive | | reps.role | object | User.UserRole, if assigned | | reps.role.id | string | — | | reps.role.name | string | — | | reps.profile | object | User.Profile, if any | | reps.profile.id | string | — | | reps.profile.name | string | — | | reps.manager | object | User.Manager, if assigned | | reps.manager.id | string | — | | reps.manager.name | string | — | | reps.target_book | object | — | | reps.target_book.id | string | — | | reps.target_book.name | string | — | | reps.metrics | object | Requested metrics keyed by metric name. Null when the caller did not request metrics. Individual values may be null for threshold-dependent metrics when the target book has no Activity Threshold configured. | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 502 | Bad Gateway | --- # Remove reps from target book `DELETE` `/api/v1/crm-connections/{crm_connection_id}/target-books/{target_book_id}/reps` Soft-deletes the active Book assignment records by setting their status to Deleted. Returns 404 if any rep is not currently assigned to this book — no reps are removed if any are not found. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | target_book_id | string | yes | Salesforce ID of the target book | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | rep_ids | array[string] | yes | Salesforce IDs of reps to remove | ## Response `204 No Content` — `application/json` _No response body._ ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 502 | Bad Gateway | --- # Post target book segment `POST` `/api/v1/crm-connections/{crm_connection_id}/target-books/{target_book_id}/segments` Returns the full updated target book. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | target_book_id | string | yes | Salesforce ID of the target book | ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | maximum_capacity | integer | no | — | | name | string | yes | — | | conditions_operator | string | yes | — | | custom_condition_logic | string | no | — | | conditions | array[object] | yes | — | | conditions.id | string | no | Salesforce ID; present = update existing, absent = create new | | conditions.field | string | yes | Salesforce field API name | | conditions.operator | string | yes | Comparison operator | | conditions.value | string | yes | Value to compare against | | is_locked | boolean | no | — | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | Salesforce ID of the target book record | | name | string | Target book name | | status | string | Status: Active or Deleted | | maximum_capacity | number | Maximum account capacity | | ownership_field | string | Salesforce ownership field API name | | created_at | string | ISO-8601 creation timestamp | | updated_at | string | ISO-8601 last-updated timestamp | | activity_threshold | object | Assigned activity threshold (if any) | | activity_threshold.id | string | Salesforce ID of the threshold definition | | activity_threshold.name | string | Name of the activity threshold | | segments | array[object] | Segments with their conditions (present when include_segments=true) | | reps | array[object] | Active assigned reps (present when include_reps=true) | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Put target book segments `PUT` `/api/v1/crm-connections/{crm_connection_id}/target-books/{target_book_id}/segments` Segments with id are updated, segments without id are created, and existing segments not in the request are deleted. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | target_book_id | string | yes | Salesforce ID of the target book | ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | segments | array[object] | yes | — | | segments.maximum_capacity | integer | no | — | | segments.name | string | yes | — | | segments.conditions_operator | string | yes | — | | segments.custom_condition_logic | string | no | — | | segments.conditions | array[object] | yes | — | | segments.conditions.id | string | no | Salesforce ID; present = update existing, absent = create new | | segments.conditions.field | string | yes | Salesforce field API name | | segments.conditions.operator | string | yes | Comparison operator | | segments.conditions.value | string | yes | Value to compare against | | segments.is_locked | boolean | no | — | | segments.id | string | no | Salesforce ID; present = update existing, absent = create new | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | Salesforce ID of the target book record | | name | string | Target book name | | status | string | Status: Active or Deleted | | maximum_capacity | number | Maximum account capacity | | ownership_field | string | Salesforce ownership field API name | | created_at | string | ISO-8601 creation timestamp | | updated_at | string | ISO-8601 last-updated timestamp | | activity_threshold | object | Assigned activity threshold (if any) | | activity_threshold.id | string | Salesforce ID of the threshold definition | | activity_threshold.name | string | Name of the activity threshold | | segments | array[object] | Segments with their conditions (present when include_segments=true) | | reps | array[object] | Active assigned reps (present when include_reps=true) | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Patch target book segment `PATCH` `/api/v1/crm-connections/{crm_connection_id}/target-books/{target_book_id}/segments/{segment_id}` All fields are optional. When conditions is provided, it replaces the segment's conditions using a diff (id present = update, absent = create, omitted from list = delete). Omitting conditions entirely leaves them unchanged. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | target_book_id | string | yes | Salesforce ID of the target book | | segment_id | string | yes | Salesforce ID of the segment | ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | conditions_operator | string | no | — | | custom_condition_logic | string | no | — | | conditions | array[object] | no | — | | maximum_capacity | integer | no | — | | name | string | no | — | | is_locked | boolean | no | — | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | Salesforce ID of the target book record | | name | string | Target book name | | status | string | Status: Active or Deleted | | maximum_capacity | number | Maximum account capacity | | ownership_field | string | Salesforce ownership field API name | | created_at | string | ISO-8601 creation timestamp | | updated_at | string | ISO-8601 last-updated timestamp | | activity_threshold | object | Assigned activity threshold (if any) | | activity_threshold.id | string | Salesforce ID of the threshold definition | | activity_threshold.name | string | Name of the activity threshold | | segments | array[object] | Segments with their conditions (present when include_segments=true) | | reps | array[object] | Active assigned reps (present when include_reps=true) | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Delete target book segment `DELETE` `/api/v1/crm-connections/{crm_connection_id}/target-books/{target_book_id}/segments/{segment_id}` Returns 400 if the segment is the last one on the book — a target book must always have at least one segment. Returns the full updated target book. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | target_book_id | string | yes | Salesforce ID of the target book | | segment_id | string | yes | Salesforce ID of the segment | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | Salesforce ID of the target book record | | name | string | Target book name | | status | string | Status: Active or Deleted | | maximum_capacity | number | Maximum account capacity | | ownership_field | string | Salesforce ownership field API name | | created_at | string | ISO-8601 creation timestamp | | updated_at | string | ISO-8601 last-updated timestamp | | activity_threshold | object | Assigned activity threshold (if any) | | activity_threshold.id | string | Salesforce ID of the threshold definition | | activity_threshold.name | string | Name of the activity threshold | | segments | array[object] | Segments with their conditions (present when include_segments=true) | | reps | array[object] | Active assigned reps (present when include_reps=true) | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 502 | Bad Gateway | --- ### Reps # List reps `GET` `/api/v1/crm-connections/{crm_connection_id}/bookbuilder/reps` Returns reps with their active target-book assignment. Each rep's ``id`` is the Salesforce User ID; a user can only be actively assigned to one target book at a time. Supports filtering by rep name, manager, target book name or id, and active status, plus cursor-based pagination via ``next_records_url``. Default page size is 50. Pass ``next_records_url`` from one response into the next request to paginate through large result sets. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | query | string | no | Full-text search across rep name and target book name. Requires at least 2 characters; shorter values return an empty result set. | | book_definition_id | string | no | Filter by target book Salesforce ID (exact match) | | manager_id | string | no | Filter by manager Salesforce User ID (exact match) | | is_active | string | no | Filter by User.IsActive. Returns reps of all statuses when omitted. | | sort_by | string | no | SOQL field on GradientWorks__Book__c (or a dotted relationship path) to sort by, e.g. GradientWorks__User__r.Name. GradientWorks__User__c is always appended as a tiebreaker unless it is already the sort_by value. | | sort_direction | string | no | ASC or DESC | | limit | string | no | Maximum number of reps to return (default: 50). | | next_records_url | string | no | Cursor returned by a previous response to fetch the next page. When provided, filter/sort/search params are ignored (the underlying SOQL query is already shaped by the cursor). Metrics params (metrics, start_date, end_date), if supplied, still apply and fetch metrics for the returned page. | | metrics | string | no | Comma-separated list of metric names to include per rep (up to 20). Alternatively, pass a single category token — `all_accounts`, `on_book`, or `off_book` — to request every metric in that category. Category tokens cannot be combined with other tokens. Requires start_date and end_date. When omitted, the `metrics` field is null in the response. | | start_date | string | no | Start of the metrics window (inclusive), formatted as YYYY-MM-DD. Required when metrics is set. | | end_date | string | no | End of the metrics window (inclusive), formatted as YYYY-MM-DD. Required when metrics is set. | | hierarchy_rollup_enabled | string | no | When true, expands metrics to include child accounts via the org's configured account hierarchy. Returns 400 if account hierarchy is not configured in the Salesforce org. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | reps | array[object] | — | | reps.id | string | Salesforce User ID of the rep | | reps.name | string | User.Name | | reps.is_active | boolean | User.IsActive | | reps.role | object | User.UserRole, if assigned | | reps.role.id | string | — | | reps.role.name | string | — | | reps.profile | object | User.Profile, if any | | reps.profile.id | string | — | | reps.profile.name | string | — | | reps.manager | object | User.Manager, if assigned | | reps.manager.id | string | — | | reps.manager.name | string | — | | reps.target_book | object | — | | reps.target_book.id | string | — | | reps.target_book.name | string | — | | reps.metrics | object | Requested metrics keyed by metric name. Null when the caller did not request metrics. Individual values may be null for threshold-dependent metrics when the target book has no Activity Threshold configured. | | next_records_url | string | Pass this value as next_records_url on the next request to fetch the following page. Null when all records have been returned. Only present when limit exceeds Salesforce's per-page maximum (2000 records). | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Get rep `GET` `/api/v1/crm-connections/{crm_connection_id}/bookbuilder/reps/{rep_id}` Returns the rep's active target-book assignment. A user can only be actively assigned to one target book at a time, so the match is unambiguous. Returns 404 if the user has no active assignment. Pass ``metrics`` with ``start_date`` and ``end_date`` to include per-rep metrics; supported metric names match GET /bookbuilder/reps. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | rep_id | string | yes | Salesforce User ID of the rep | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | metrics | string | no | Comma-separated list of metric names to include (up to 20). Alternatively, pass a single category token — `all_accounts`, `on_book`, or `off_book` — to request every metric in that category. Category tokens cannot be combined with other tokens. Requires start_date and end_date. When omitted, the `metrics` field is null in the response. Supported metric names match GET /bookbuilder/reps. | | start_date | string | no | Start of the metrics window (inclusive), formatted as YYYY-MM-DD. Required when metrics is set. | | end_date | string | no | End of the metrics window (inclusive), formatted as YYYY-MM-DD. Required when metrics is set. | | hierarchy_rollup_enabled | string | no | When true, expands metrics to include child accounts via the org's configured account hierarchy. Returns 400 if account hierarchy is not configured in the Salesforce org. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | Salesforce User ID of the rep | | name | string | User.Name | | is_active | boolean | User.IsActive | | role | object | User.UserRole, if assigned | | role.id | string | — | | role.name | string | — | | profile | object | User.Profile, if any | | profile.id | string | — | | profile.name | string | — | | manager | object | User.Manager, if assigned | | manager.id | string | — | | manager.name | string | — | | target_book | object | — | | target_book.id | string | — | | target_book.name | string | — | | metrics | object | Requested metrics keyed by metric name. Null when the caller did not request metrics. Individual values may be null for threshold-dependent metrics when the target book has no Activity Threshold configured. | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # List rep accounts `GET` `/api/v1/crm-connections/{crm_connection_id}/bookbuilder/reps/{rep_id}/accounts` Returns a paginated list of accounts owned by the rep, optionally scoped to on-book or off-book accounts, with optional per-account metrics. See the ``metrics`` parameter for the list of supported metric names. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | rep_id | string | yes | Salesforce User ID of the rep | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | metrics | string | no | Comma-separated list of per-account metric names. Supported: total_activities, contacts_reached, open_opps, open_pipeline, created_opps, created_pipeline, activity_threshold_status, per_activity_count. When per_activity_count is included the response returns a list of {activity_id, activity_name, count} per account. start_date and end_date are required unless every requested metric is a snapshot (open_opps, open_pipeline). | | start_date | string | no | Start of the metrics window (inclusive), formatted as YYYY-MM-DD. Required when a time-scoped metric is requested. | | end_date | string | no | End of the metrics window (inclusive), formatted as YYYY-MM-DD. Required when a time-scoped metric is requested. | | scope | string | no | Filter to accounts in the rep's book. | | query | string | no | Partial-match name search across the rep's accounts. | | sort_by | string | no | Account SObject field to sort by (e.g. Name, CreatedDate). Metric names are not supported as sort keys. | | sort_direction | string | no | ASC or DESC | | offset | integer | no | Pagination offset (default 0, max 2000). | | limit | integer | no | Page size (default 50). | | fields | string | no | Comma-separated list of additional Account fields to include in the response (up to 20). Supports dotted relationship paths, e.g. Owner.Name. Returned per account under the `fields` key. | | hierarchy_rollup_enabled | string | no | When true, expands the account list to include child accounts via the org's configured account hierarchy, with a children array per parent account. Returns 400 if account hierarchy is not configured in the Salesforce org. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | accounts | array[object] | — | | accounts.id | string | — | | accounts.name | string | — | | accounts.fields | object | Requested additional Account fields keyed by field name. Null when the caller did not request fields. | | accounts.metrics | object | Requested metrics keyed by metric name. Null when the caller did not request metrics. Scalar metrics map to int/float/string; per_activity_count maps to a list of {activity_id, activity_name, count}. | | accounts.children | array[object] | Child accounts rolled up under this account when hierarchy_rollup_enabled is true. Null when hierarchy rollup is not enabled. | | offset | integer | — | | limit | integer | — | | total | integer | — | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # List rep opportunities `GET` `/api/v1/crm-connections/{crm_connection_id}/bookbuilder/reps/{rep_id}/opportunities` Returns a paginated list of opportunities for the rep, filtered by ``type``: ``open`` (currently open) or ``created`` (created within a date window). The ``scope`` query param filters to opportunities on accounts in the rep's book (default: all accounts). ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | rep_id | string | yes | Salesforce User ID of the rep | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | type | string | yes | `open` returns currently-open opportunities; `created` returns opportunities created within the date window. start_date and end_date are required when type=created and rejected when type=open. | | start_date | string | no | Start of the created-on window (inclusive), formatted as YYYY-MM-DD. Required when type=created; not allowed when type=open. | | end_date | string | no | End of the created-on window (inclusive), formatted as YYYY-MM-DD. Required when type=created; not allowed when type=open. | | scope | string | no | Filter to opportunities whose account is in the rep's book. | | sort_by | string | no | Opportunity SObject field to sort by (e.g. Name, StageName, Account.Name, CreatedDate). | | sort_direction | string | no | ASC or DESC | | offset | integer | no | Pagination offset (default 0, max 2000). | | limit | integer | no | Page size (default 50). | | hierarchy_rollup_enabled | string | no | When true, expands opportunity results to include child accounts via the org's configured account hierarchy. Only applies when type=open; ignored for type=created. Returns 400 if account hierarchy is not configured in the Salesforce org. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | opportunities | array[object] | — | | opportunities.id | string | — | | opportunities.name | string | — | | opportunities.account | object | — | | opportunities.account.id | string | — | | opportunities.account.name | string | — | | opportunities.owner | object | — | | opportunities.owner.id | string | — | | opportunities.owner.name | string | — | | opportunities.creator | object | Reflects the configured opportunity creator field, falling back to CreatedById. | | opportunities.creator.id | string | — | | opportunities.creator.name | string | — | | opportunities.type | string | — | | opportunities.stage_name | string | — | | opportunities.amount | number | Reflects the configured opportunity value field. Null when no opportunity value field is configured. | | offset | integer | — | | limit | integer | — | | total | integer | — | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Get rep activities by day chart `GET` `/api/v1/crm-connections/{crm_connection_id}/bookbuilder/reps/{rep_id}/charts/activities-by-day` Returns a per-day count of completed activities (tasks and events) for the rep over the requested date window, in the running user's timezone. Days with zero activity are gap-filled. The ``scope`` query param filters to activity on all accounts (default), the rep's on-book accounts, or off-book accounts. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | rep_id | string | yes | Salesforce User ID of the rep | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | start_date | string | yes | Start of the date window (inclusive), formatted as YYYY-MM-DD. | | end_date | string | yes | End of the date window (inclusive), formatted as YYYY-MM-DD. | | scope | string | no | Filter to activities on accounts in the rep's book. | | hierarchy_rollup_enabled | string | no | When true, expands chart data to include child-account activity via the org's configured account hierarchy. Returns 400 if account hierarchy is not configured in the Salesforce org. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | chart | string | Chart identifier (e.g. activities-by-day) | | start_date | string | — | | end_date | string | — | | data | object | Map of YYYY-MM-DD → count for that day. Days with zero activity are gap-filled with 0. | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Get rep created opps by day chart `GET` `/api/v1/crm-connections/{crm_connection_id}/bookbuilder/reps/{rep_id}/charts/created-opps-by-day` Returns a per-day count of opportunities created by the rep over the requested date window, in the running user's timezone. Opportunity selection respects the rep pipeline criteria configured in the Gradient Works managed package; "created by" reflects the configured opportunity- creator field if set, else ``CreatedById``. Days with zero activity are gap-filled. The ``scope`` query param filters to opportunities whose account is in the rep's book (default: all accounts). ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | rep_id | string | yes | Salesforce User ID of the rep | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | start_date | string | yes | Start of the date window (inclusive), formatted as YYYY-MM-DD. | | end_date | string | yes | End of the date window (inclusive), formatted as YYYY-MM-DD. | | scope | string | no | Filter to opportunities whose account is in the rep's book. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | chart | string | Chart identifier (e.g. activities-by-day) | | start_date | string | — | | end_date | string | — | | data | object | Map of YYYY-MM-DD → count for that day. Days with zero activity are gap-filled with 0. | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- ### Distributions # List distribution templates `GET` `/api/v1/crm-connections/{crm_connection_id}/distributions/templates` Return distribution templates for the given Salesforce CRM connection. Supports sorting and cursor-based pagination. Default page size is 50. Pass next_records_url from one response into the next request to paginate through large result sets. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | query | string | no | Search by template name (min 2 characters) | | sort_by | string | no | Field to sort by (default: LastModifiedDate) | | sort_direction | string | no | Sort direction: ASC or DESC (default: DESC) | | limit | string | no | Maximum number of templates to return (default: 50). | | next_records_url | string | no | Cursor returned by a previous response to fetch the next page. When provided, all other query params are ignored. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | templates | array[object] | List of distribution template records | | templates.id | string | Distribution template id | | templates.name | string | Template name | | templates.created_at | string | ISO-8601 creation timestamp | | templates.updated_at | string | ISO-8601 last-updated timestamp | | templates.recipients | array[object] | Recipients assigned to this template — active reps or active target books | | templates.recipients.type | string | Recipient type: 'Rep' or 'Target Book' | | templates.recipients.id | string | Salesforce ID of the recipient | | templates.recipients.name | string | Display name | | templates.conditions_operator | string | How conditions are combined: AND, OR, or CUSTOM | | templates.custom_condition_logic | string | Custom condition expression (e.g. '1 AND (2 OR 3)'), present when conditions_operator is CUSTOM | | templates.conditions | array[object] | Conditions for this template | | templates.conditions.id | string | Salesforce ID of the condition record | | templates.conditions.number | integer | Condition number, used in custom expressions | | templates.conditions.field | string | Salesforce field API name | | templates.conditions.operator | string | Comparison operator | | templates.conditions.value | string | Value to compare against | | next_records_url | string | Pass this value as next_records_url on the next request to fetch the following page. Null when all records have been returned. Only present when limit exceeds Salesforce's per-page maximum (2000 records). | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Get distribution `GET` `/api/v1/crm-connections/{crm_connection_id}/distributions/{distribution_id}` Returns the full configuration for a distribution definition or template, including conditions, ranking criteria, update fields, recipients, and schedule info. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | distribution_id | string | yes | Salesforce ID of the distribution definition | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | Salesforce ID of the distribution definition | | name | string | Distribution name | | status | string | Status: Active or Deleted | | is_template | boolean | Whether this is a template definition | | distribution_type | string | Distribution method, e.g. Round Robin | | distribution_amount | number | Number of records to distribute per recipient | | source | string | Record source, e.g. ACCOUNT_POOL or USER | | created_at | string | ISO-8601 creation timestamp | | updated_at | string | ISO-8601 last-updated timestamp | | recipients | array[object] | Recipients: active reps or active target books | | recipients.type | string | Recipient type: 'Rep' or 'Target Book' | | recipients.id | string | Salesforce ID of the recipient | | recipients.name | string | Display name | | conditions_operator | string | How conditions are combined: AND, OR, or CUSTOM | | custom_condition_logic | string | Custom condition expression (e.g. '1 AND (2 OR 3)'), present when conditions_operator is CUSTOM | | conditions | array[object] | Eligibility conditions | | conditions.id | string | Salesforce ID of the condition record | | conditions.number | integer | Condition number, used in custom expressions | | conditions.field | string | Salesforce field API name | | conditions.operator | string | Comparison operator | | conditions.value | string | Value to compare against | | ranking | array[object] | Ranking criteria | | ranking.id | string | Salesforce ID of the ranking condition | | ranking.number | integer | Ranking position number | | ranking.field | string | Salesforce field API name to rank by | | ranking.order | string | Sort order: ASC or DESC | | update_fields | array[object] | Fields to update on distributed records | | update_fields.id | string | Salesforce ID of the field record | | update_fields.name | string | Salesforce field API name to update | | update_fields.value | string | Value to set | | update_fields.data_type | string | Data type of the field | | schedule | object | Schedule configuration (null if no schedule) | | schedule.type | string | Schedule type: 'one_time' or 'recurring' | | schedule.next_run_time | string | ISO-8601 next scheduled run time | | schedule.interval | integer | Recurrence interval (only for recurring) | | schedule.interval_type | string | Recurrence interval unit, e.g. 'Days' (only for recurring) | | schedule.enabled | boolean | Whether the recurring schedule is active (only for recurring) | | gw_list | object | Linked Gradient Works list (null if none) | | gw_list.id | string | Gradient Works list ID | | gw_list.name | string | List name | | gw_list.source | string | List source | | source_users | array[object] | Source users: account pool users when source is ACCOUNT_POOL, the single source user when source is USER, null otherwise | | assignment_order | object | Round-robin assignment order with next-up indicator (null if no round-robin entries exist) | | assignment_order.start_position | integer | 1-based position of the next rep to be assigned | | assignment_order.entries | array[object] | Ordered list of reps in assignment order | | assignment_order.entries.position | integer | 1-based position in the assignment order | | assignment_order.entries.rep | string | Rep display name | | assignment_order.entries.target_book | string | Target book name | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 502 | Bad Gateway | --- # List runs `GET` `/api/v1/crm-connections/{crm_connection_id}/distributions/runs` Return distribution runs for the given Salesforce CRM connection. Optionally filter by status. Supports sorting and cursor-based pagination. Default page size is 50. Pass next_records_url from one response into the next request to paginate through large result sets. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | query | string | no | Search by distribution definition name (min 2 characters) | | status | string | no | Filter by run status: Pending, In Progress, Completed (default), or Failed | | start_date | string | no | Include runs distributed on or after this date (YYYY-MM-DD) | | end_date | string | no | Include runs distributed on or before this date (YYYY-MM-DD) | | sort_by | string | no | Field to sort by: distributed_at (default), created_at, updated_at, or status | | sort_direction | string | no | Sort direction: ASC or DESC (default: DESC) | | limit | string | no | Maximum number of runs to return (default: 50). | | next_records_url | string | no | Cursor returned by a previous response to fetch the next page. When provided, all other query params are ignored. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | runs | array[object] | List of distribution run records | | runs.id | string | Run record ID | | runs.definition | object | Distribution definition that produced this run | | runs.definition.id | string | Salesforce ID of the definition | | runs.definition.name | string | Definition name | | runs.status | string | Run status: Pending, In Progress, Completed, or Failed | | runs.status_message | string | Status message (typically set on failure) | | runs.distributed_at | string | ISO-8601 timestamp when distribution ran | | runs.created_at | string | ISO-8601 creation timestamp | | runs.updated_at | string | ISO-8601 last-modified timestamp | | runs.distributed_count | integer | Total accounts assigned in this run | | next_records_url | string | Pass this value as next_records_url on the next request to fetch the following page. Null when all records have been returned. Only present when limit exceeds Salesforce's per-page maximum (2000 records). | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Get run `GET` `/api/v1/crm-connections/{crm_connection_id}/distributions/runs/{run_id}` Returns the run summary, including status, timestamps, definition reference, market coverage report, and total assignment count. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | run_id | string | yes | Salesforce ID of the distribution run | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | Run record ID | | definition | object | Distribution definition that produced this run | | definition.id | string | Salesforce ID of the definition | | definition.name | string | Definition name | | status | string | Run status: Pending, In Progress, Completed, or Failed | | status_message | string | Status message (typically set on failure) | | run_source | string | How the distribution was triggered | | distributed_at | string | ISO-8601 timestamp when distribution ran | | created_at | string | ISO-8601 creation timestamp | | updated_at | string | ISO-8601 last-modified timestamp | | distributed_count | integer | Total accounts assigned in this run | | market_coverage_report | object | Linked market coverage report (null if none) | | market_coverage_report.id | string | Salesforce ID of the report definition | | market_coverage_report.name | string | Report name | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # List run assignments `GET` `/api/v1/crm-connections/{crm_connection_id}/distributions/runs/{run_id}/assignments` Returns paginated assignment records for a distribution run. Each assignment includes the assigned record, new/previous owner, and book definition matches (target book + segment). ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | run_id | string | yes | Salesforce ID of the distribution run | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | limit | string | no | Maximum number of assignments to return per page (default: 50). | | next_records_url | string | no | Cursor returned by a previous response to fetch the next page of assignments. When provided, all other query params are ignored. | | sort_by | string | no | Field to sort assignments by: assigned_at (default), record_name, new_owner_name, previous_owner_name, created_at, or updated_at | | sort_direction | string | no | Sort direction: DESC (default) or ASC | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | assignments | array[object] | Assignment records | | assignments.id | string | Salesforce ID of the assignment record | | assignments.name | string | Record name | | assignments.created_at | string | ISO-8601 creation timestamp | | assignments.updated_at | string | ISO-8601 last-modified timestamp | | assignments.assignment_type | string | Assignment type: Queue, Direct, or Other | | assignments.assigned_time | string | ISO-8601 timestamp when the assignment was made | | assignments.assigned_by | object | User who triggered the assignment | | assignments.assigned_by.id | string | Salesforce User ID | | assignments.assigned_by.name | string | User display name | | assignments.record | object | The CRM record that was assigned | | assignments.record.id | string | Salesforce ID of the assigned record | | assignments.record.type | string | Salesforce object type (Account, Contact, Lead, Opportunity) | | assignments.record.field | string | Salesforce field updated by the assignment | | assignments.record.name | string | Name of the assigned record | | assignments.new_owner | object | User the record was assigned to | | assignments.new_owner.id | string | Salesforce User ID | | assignments.new_owner.name | string | User display name | | assignments.previous_owner | object | Previous owner (null if record had no prior owner) | | assignments.previous_owner.id | string | Salesforce User ID | | assignments.previous_owner.name | string | User display name | | assignments.queue | object | Queue context (null for non-Queue assignments) | | assignments.queue.id | string | Salesforce Queue ID | | assignments.queue.name | string | Queue name | | assignments.queue.item_id | string | Salesforce ID of the associated Gradient Works queue item | | assignments.request_id | string | Request ID associated with this assignment | | assignments.flow | object | Flow context (null when the assignment was not triggered by a Salesforce Flow) | | assignments.flow.name | string | Name of the flow that ran | | assignments.flow.version | number | Version of the flow that ran | | assignments.flow.version_durable_id | string | Durable ID of the flow version | | assignments.flow.interview_guid | string | GUID of the flow interview | | assignments.distribution | object | Distribution run that produced this assignment (null if not from a distribution) | | assignments.distribution.id | string | Salesforce ID of the run | | assignments.distribution.name | string | Name of the distribution or retrieval definition | | assignments.retrieval | object | Retrieval run that produced this assignment (null if not from a retrieval) | | assignments.retrieval.id | string | Salesforce ID of the run | | assignments.retrieval.name | string | Name of the distribution or retrieval definition | | assignments.detail | string | Additional assignment detail | | assignments.book_definition_matches | array[object] | Target books and segments matched for this assignment. Only populated for distribution assignments when include_book_matches=true; null for retrievals and manual or queue assignments. | | next_records_url | string | Pass this value as next_records_url on the next request to fetch the following page. Null when all records have been returned. | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- ### Queues # List queues `GET` `/api/v1/crm-connections/{crm_connection_id}/queues` Return Gradient Works queues for the given Salesforce CRM connection. Supports filtering by assignment policy, target book queue flag, async flag, capacity meter, and name search. Default page size is 50. Pass next_records_url from one response into the next request to paginate through large result sets. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | policy | string | no | Filter by assignment policy. Comma-separated list of: Adaptive, Round Robin, Schedule First Available. | | is_target_book_queue | string | no | When true, returns only target book queues (Type__c = 'Target Book'). When false, returns only standard queues (Type__c is null or any other value). Omit to return all queues. | | is_async | string | no | Filter by whether assignments are processed asynchronously | | capacity_meter_id | string | no | Filter by the Salesforce ID of the linked capacity meter | | query | string | no | Search by queue name (min 2 characters) | | sort_by | string | no | SOQL field on GradientWorks__Queue__c (or a dotted relationship path) to sort by, e.g. GradientWorks__Capacity_Meter__r.Name. Default: LastModifiedDate. | | sort_direction | string | no | ASC or DESC | | limit | string | no | Maximum number of queues to return (default: 50). | | next_records_url | string | no | Cursor returned by a previous response to fetch the next page. When provided, all other query params are ignored. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | queues | array[object] | List of queue records | | queues.id | string | Salesforce ID of the queue record | | queues.name | string | Queue name | | queues.type | string | Raw value of the Salesforce Type__c field. Typically null (standard queue) or 'Target Book' (target book queue); other values may appear if set directly in Salesforce. | | queues.assignment_policy | string | Assignment policy: Adaptive, Round Robin, or Schedule First Available | | queues.assignment_field | string | Field on the assigned record that stores the assigned user | | queues.capacity_meter | object | Linked capacity meter (null if none) | | queues.capacity_meter.id | string | Salesforce ID of the capacity meter | | queues.capacity_meter.name | string | Capacity meter name | | queues.last_assignment_time | string | ISO-8601 timestamp of the most recent assignment | | queues.next_slot_index | number | Next round-robin slot index | | queues.post_assignment_automation | string | API name of a Flow to execute after assignments complete for this queue | | queues.async_assignment | boolean | Whether assignments for this queue are always performed asynchronously. Target book queues always use asynchronous assignment. | | queues.standard_queue_id | string | Salesforce ID of the linked standard Group (Queue) | | queues.user_weight_field | string | Field on User used to weight round-robin distribution | | queues.user_maximum_capacity | number | Default maximum capacity per user for this queue's members | | queues.user_maximum_capacity_field | string | DEPRECATED — name of the field on User formerly used to source per-user maximum capacity. No longer read by the managed package; retained for troubleshooting only. | | queues.user_used_capacity_field | string | DEPRECATED — name of the field on User formerly used to source per-user used capacity. No longer read by the managed package; retained for troubleshooting only. | | queues.created_at | string | ISO-8601 creation timestamp | | queues.updated_at | string | ISO-8601 last-updated timestamp | | next_records_url | string | Pass this value as next_records_url on the next request to fetch the following page. Null when all records have been returned. Only present when limit exceeds Salesforce's per-page maximum (2000 records). | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Get queue `GET` `/api/v1/crm-connections/{crm_connection_id}/queues/{queue_id}` Returns the full configuration for a Gradient Works queue, its queue user members, the round-robin slot ordering, and any target books served by the queue. All of these are fetched in one Salesforce call via parent + subqueries. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | queue_id | string | yes | Salesforce ID of the queue | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | queue | object | Queue configuration | | queue.id | string | Salesforce ID of the queue record | | queue.name | string | Queue name | | queue.type | string | Raw value of the Salesforce Type__c field. Typically null (standard queue) or 'Target Book' (target book queue); other values may appear if set directly in Salesforce. | | queue.assignment_policy | string | Assignment policy: Adaptive, Round Robin, or Schedule First Available | | queue.assignment_field | string | Field on the assigned record that stores the assigned user | | queue.capacity_meter | object | Linked capacity meter (null if none) | | queue.capacity_meter.id | string | Salesforce ID of the capacity meter | | queue.capacity_meter.name | string | Capacity meter name | | queue.last_assignment_time | string | ISO-8601 timestamp of the most recent assignment | | queue.next_slot_index | number | Next round-robin slot index | | queue.post_assignment_automation | string | API name of a Flow to execute after assignments complete for this queue | | queue.async_assignment | boolean | Whether assignments for this queue are always performed asynchronously. Target book queues always use asynchronous assignment. | | queue.standard_queue_id | string | Salesforce ID of the linked standard Group (Queue) | | queue.user_weight_field | string | Field on User used to weight round-robin distribution | | queue.user_maximum_capacity | number | Default maximum capacity per user for this queue's members | | queue.user_maximum_capacity_field | string | DEPRECATED — name of the field on User formerly used to source per-user maximum capacity. No longer read by the managed package; retained for troubleshooting only. | | queue.user_used_capacity_field | string | DEPRECATED — name of the field on User formerly used to source per-user used capacity. No longer read by the managed package; retained for troubleshooting only. | | queue.created_at | string | ISO-8601 creation timestamp | | queue.updated_at | string | ISO-8601 last-updated timestamp | | queue_users | array[object] | Queue user members of this queue, sorted ascending by the linked Salesforce User's Name. Empty list when the queue has no members. | | queue_users.id | string | Salesforce ID of the queue user record | | queue_users.user | object | Linked Salesforce User (id + name). Null when the underlying Salesforce User has been deleted. | | queue_users.user.id | string | Salesforce ID of the referenced record | | queue_users.user.name | string | Display name (null if the relationship is empty) | | queue_users.status | string | Raw Salesforce Status__c value: 'Available' or 'Unavailable' | | queue_users.weight | integer | Round-robin weight (higher weight yields more queue slots) | | queue_users.last_assignment_time | string | ISO-8601 timestamp of the most recent assignment | | queue_users.maximum_capacity | number | Per-user maximum capacity for this queue | | queue_users.used_capacity | number | Currently used capacity | | queue_users.available_capacity | number | Remaining capacity (formula field; null when capacity fields are unset) | | queue_users.target_book | object | Target book this queue user is tied to, if any. Null when unset or when the underlying target book was deleted. | | queue_users.target_book.id | string | Salesforce ID of the target book | | queue_users.target_book.name | string | Target book name (null if the target book was deleted) | | queue_users.working_hours_start | string | Start of working hours window | | queue_users.working_hours_end | string | End of working hours window | | queue_users.unavailable_until_time | string | If set, the user is marked unavailable until this ISO-8601 timestamp | | queue_users.skip_next_times | number | Number of upcoming round-robin turns this user will skip | | queue_slots | array[object] | Round-robin slot ordering, sorted ascending by slot index. A queue user with weight > 1 occupies more than one slot. Join slots back to members via queue_user.id == queue_users[].id. | | queue_slots.index | integer | 0-based round-robin slot index | | queue_slots.queue_user | object | The queue user that occupies this slot. `id` is the queue user ID (join against queue_users[].id); `name` is the linked Salesforce User's Name. | | queue_slots.queue_user.id | string | Salesforce ID of the referenced record | | queue_slots.queue_user.name | string | Display name (null if the relationship is empty) | | target_books | array[object] | Active target books served by this queue, sorted ascending by name. Always empty for standard queues (type != 'Target Book'). | | target_books.id | string | Salesforce ID of the target book | | target_books.name | string | Target book name (null if the target book was deleted) | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- ### Retrievals # List retrieval templates `GET` `/api/v1/crm-connections/{crm_connection_id}/retrievals/templates` Return retrieval templates for the given Salesforce CRM connection. Supports sorting and cursor-based pagination. Default page size is 50. Pass next_records_url from one response into the next request to paginate through large result sets. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | query | string | no | Search by template name (min 2 characters) | | sort_by | string | no | Field to sort by (default: LastModifiedDate) | | sort_direction | string | no | Sort direction: ASC or DESC (default: DESC) | | limit | string | no | Maximum number of templates to return (default: 50). | | next_records_url | string | no | Cursor returned by a previous response to fetch the next page. When provided, all other query params are ignored. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | templates | array[object] | List of retrieval template records | | templates.id | string | Retrieval template id | | templates.name | string | Template name | | templates.created_at | string | ISO-8601 creation timestamp | | templates.updated_at | string | ISO-8601 last-updated timestamp | | templates.retrieve_from | array[object] | Sources to retrieve from — reps or target books, including inactive reps | | templates.retrieve_from.type | string | Source type: 'Rep' or 'Target Book' | | templates.retrieve_from.id | string | Salesforce ID of the source | | templates.retrieve_from.name | string | Display name | | templates.conditions_operator | string | How conditions are combined: AND, OR, or CUSTOM | | templates.custom_condition_logic | string | Custom condition expression (e.g. '1 AND (2 OR 3)'), present when conditions_operator is CUSTOM | | templates.conditions | array[object] | Conditions for this template | | templates.conditions.id | string | Salesforce ID of the condition record | | templates.conditions.number | integer | Condition number, used in custom expressions | | templates.conditions.field | string | Salesforce field API name | | templates.conditions.operator | string | Comparison operator | | templates.conditions.value | string | Value to compare against | | next_records_url | string | Pass this value as next_records_url on the next request to fetch the following page. Null when all records have been returned. Only present when limit exceeds Salesforce's per-page maximum (2000 records). | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Get retrieval `GET` `/api/v1/crm-connections/{crm_connection_id}/retrievals/{retrieval_id}` Returns the full configuration for a retrieval definition or template, including conditions, ranking criteria, update fields, retrieve-from sources, and schedule info. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | retrieval_id | string | yes | Salesforce ID of the retrieval definition | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | Salesforce ID of the retrieval definition | | name | string | Retrieval name | | status | string | Status: Active or Deleted | | is_template | boolean | Whether this is a template definition | | ownership_field | string | Salesforce field API name for ownership assignment | | max_retrieval_amount | number | Maximum number of records to retrieve per source | | retrieval_type | string | Retrieval type: On Book or Off Book | | created_at | string | ISO-8601 creation timestamp | | updated_at | string | ISO-8601 last-updated timestamp | | retrieve_from | array[object] | Sources to retrieve from: active reps or active target books | | retrieve_from.type | string | Source type: 'Rep' or 'Target Book' | | retrieve_from.id | string | Salesforce ID of the source | | retrieve_from.name | string | Display name | | conditions_operator | string | How conditions are combined: AND, OR, or CUSTOM | | custom_condition_logic | string | Custom condition expression (e.g. '1 AND (2 OR 3)'), present when conditions_operator is CUSTOM | | conditions | array[object] | Eligibility conditions | | conditions.id | string | Salesforce ID of the condition record | | conditions.number | integer | Condition number, used in custom expressions | | conditions.field | string | Salesforce field API name | | conditions.operator | string | Comparison operator | | conditions.value | string | Value to compare against | | ranking | array[object] | Ranking criteria | | ranking.id | string | Salesforce ID of the ranking condition | | ranking.number | integer | Ranking position number | | ranking.field | string | Salesforce field API name to rank by | | ranking.order | string | Sort order: ASC or DESC | | update_fields | array[object] | Fields to update on retrieved records | | update_fields.id | string | Salesforce ID of the field record | | update_fields.name | string | Salesforce field API name to update | | update_fields.value | string | Value to set | | update_fields.data_type | string | Data type of the field | | schedule | object | Schedule configuration (null if no schedule) | | schedule.type | string | Schedule type: 'one_time' or 'recurring' | | schedule.next_run_time | string | ISO-8601 next scheduled run time | | schedule.interval | integer | Recurrence interval (only for recurring) | | schedule.interval_type | string | Recurrence interval unit, e.g. 'Days' (only for recurring) | | schedule.enabled | boolean | Whether the recurring schedule is active (only for recurring) | | new_owners | array[object] | New owners: account pool users when source is ACCOUNT_POOL, the configured source user when source is USER, null otherwise | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 502 | Bad Gateway | --- # List runs `GET` `/api/v1/crm-connections/{crm_connection_id}/retrievals/runs` Return retrieval runs for the given Salesforce CRM connection. Optionally filter by status. Supports sorting and cursor-based pagination. Default page size is 50. Pass next_records_url from one response into the next request to paginate through large result sets. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | query | string | no | Search by retrieval definition name (min 2 characters) | | status | string | no | Filter by run status: Pending, In Progress, Completed (default), or Failed | | start_date | string | no | Include runs retrieved on or after this date (YYYY-MM-DD) | | end_date | string | no | Include runs retrieved on or before this date (YYYY-MM-DD) | | sort_by | string | no | Field to sort by: retrieved_at (default), created_at, updated_at, or status | | sort_direction | string | no | Sort direction: ASC or DESC (default: DESC) | | limit | string | no | Maximum number of runs to return (default: 50). | | next_records_url | string | no | Cursor returned by a previous response to fetch the next page. When provided, all other query params are ignored. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | runs | array[object] | List of retrieval run records | | runs.id | string | Run record ID | | runs.definition | object | Retrieval definition that produced this run | | runs.definition.id | string | Salesforce ID of the definition | | runs.definition.name | string | Definition name | | runs.status | string | Run status: Pending, In Progress, Completed, or Failed | | runs.status_message | string | Status message (typically set on failure) | | runs.retrieved_at | string | ISO-8601 timestamp when retrieval ran | | runs.created_at | string | ISO-8601 creation timestamp | | runs.updated_at | string | ISO-8601 last-modified timestamp | | runs.retrieved_count | integer | Total accounts retrieved in this run | | next_records_url | string | Pass this value as next_records_url on the next request to fetch the following page. Null when all records have been returned. Only present when limit exceeds Salesforce's per-page maximum (2000 records). | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Get run `GET` `/api/v1/crm-connections/{crm_connection_id}/retrievals/runs/{run_id}` Returns the run summary, including status, timestamps, definition reference, market coverage report, flow details, and total assignment count. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | run_id | string | yes | Salesforce ID of the retrieval run | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | Run record ID | | definition | object | Retrieval definition that produced this run | | definition.id | string | Salesforce ID of the definition | | definition.name | string | Definition name | | status | string | Run status: Pending, In Progress, Completed, or Failed | | status_message | string | Status message (typically set on failure) | | run_source | string | How the retrieval was triggered | | retrieved_at | string | ISO-8601 timestamp when retrieval ran | | created_at | string | ISO-8601 creation timestamp | | updated_at | string | ISO-8601 last-modified timestamp | | retrieved_count | integer | Total accounts retrieved in this run | | market_coverage_report | object | Linked market coverage report (null if none) | | market_coverage_report.id | string | Salesforce ID of the report definition | | market_coverage_report.name | string | Report name | | flow | object | Flow execution details (null if no flow fields populated) | | flow.interview_guid | string | Flow interview GUID | | flow.name | string | Flow name | | flow.version | string | Flow version number | | flow.version_view_durable_id | string | Flow version view durable ID | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # List run assignments `GET` `/api/v1/crm-connections/{crm_connection_id}/retrievals/runs/{run_id}/assignments` Returns paginated assignment records for a retrieval run. Each assignment includes the assigned record, new/previous owner. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | run_id | string | yes | Salesforce ID of the retrieval run | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | limit | string | no | Maximum number of assignments to return per page (default: 50). | | next_records_url | string | no | Cursor returned by a previous response to fetch the next page of assignments. When provided, all other query params are ignored. | | sort_by | string | no | Field to sort assignments by: assigned_at (default), record_name, new_owner_name, previous_owner_name, created_at, or updated_at | | sort_direction | string | no | Sort direction: DESC (default) or ASC | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | assignments | array[object] | Assignment records | | assignments.id | string | Salesforce ID of the assignment record | | assignments.name | string | Record name | | assignments.created_at | string | ISO-8601 creation timestamp | | assignments.updated_at | string | ISO-8601 last-modified timestamp | | assignments.assignment_type | string | Assignment type: Queue, Direct, or Other | | assignments.assigned_time | string | ISO-8601 timestamp when the assignment was made | | assignments.assigned_by | object | User who triggered the assignment | | assignments.assigned_by.id | string | Salesforce User ID | | assignments.assigned_by.name | string | User display name | | assignments.record | object | The CRM record that was assigned | | assignments.record.id | string | Salesforce ID of the assigned record | | assignments.record.type | string | Salesforce object type (Account, Contact, Lead, Opportunity) | | assignments.record.field | string | Salesforce field updated by the assignment | | assignments.record.name | string | Name of the assigned record | | assignments.new_owner | object | User the record was assigned to | | assignments.new_owner.id | string | Salesforce User ID | | assignments.new_owner.name | string | User display name | | assignments.previous_owner | object | Previous owner (null if record had no prior owner) | | assignments.previous_owner.id | string | Salesforce User ID | | assignments.previous_owner.name | string | User display name | | assignments.queue | object | Queue context (null for non-Queue assignments) | | assignments.queue.id | string | Salesforce Queue ID | | assignments.queue.name | string | Queue name | | assignments.queue.item_id | string | Salesforce ID of the associated Gradient Works queue item | | assignments.request_id | string | Request ID associated with this assignment | | assignments.flow | object | Flow context (null when the assignment was not triggered by a Salesforce Flow) | | assignments.flow.name | string | Name of the flow that ran | | assignments.flow.version | number | Version of the flow that ran | | assignments.flow.version_durable_id | string | Durable ID of the flow version | | assignments.flow.interview_guid | string | GUID of the flow interview | | assignments.distribution | object | Distribution run that produced this assignment (null if not from a distribution) | | assignments.distribution.id | string | Salesforce ID of the run | | assignments.distribution.name | string | Name of the distribution or retrieval definition | | assignments.retrieval | object | Retrieval run that produced this assignment (null if not from a retrieval) | | assignments.retrieval.id | string | Salesforce ID of the run | | assignments.retrieval.name | string | Name of the distribution or retrieval definition | | assignments.detail | string | Additional assignment detail | | assignments.book_definition_matches | array[object] | Target books and segments matched for this assignment. Only populated for distribution assignments when include_book_matches=true; null for retrievals and manual or queue assignments. | | next_records_url | string | Pass this value as next_records_url on the next request to fetch the following page. Null when all records have been returned. | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- ### Carve / Projects # Get projects `GET` `/api/v1/carve/projects` Return the active carve projects in the account linked to the API key that the key's user may see. ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | — | | name | string | — | | status | string | — | | is_sample | boolean | — | | created_at | string | — | | updated_at | string | — | ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | --- # Create project `POST` `/api/v1/carve/projects` Returns the created project. The project is immediately active and ready to receive data sources and scenarios. ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | name | string | yes | Name for the new project | ## Response `201 Created` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | — | | name | string | — | | status | string | — | | is_sample | boolean | — | | created_at | string | — | | updated_at | string | — | ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | | 422 | Unprocessable Entity | | 500 | Internal Server Error | --- # Get project `GET` `/api/v1/carve/projects/{project_id}` Return a single project by ID. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | — | | name | string | — | | status | string | — | | is_sample | boolean | — | | created_at | string | — | | updated_at | string | — | ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | | 404 | Not Found | --- # Get project data sources `GET` `/api/v1/carve/projects/{project_id}/data-sources` Return all active data sources attached to the given project. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | — | | name | string | — | | type | string | — | | is_primary | boolean | — | | created_at | string | — | | updated_at | string | — | ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | | 404 | Not Found | --- # Attach project data source `POST` `/api/v1/carve/projects/{project_id}/data-sources` Attach an existing CSV data source to the project. The data source must belong to the same account as the project. Pass an asset:// URI as source.id — the UUID is returned by the Assets endpoint. Only csv sources are currently supported. The first data source attached to a project is automatically set as the primary data source. If the account has a Carve subscription with a row limit, that limit is enforced when attaching the primary data source. Set `use_credits` to true to allow the data source to exceed the row limit by charging credits for the overage (1 credit per 10 rows above the limit). Returns 422 if the account has insufficient credits. The account sheet will need to be refreshed to reflect any changes. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | source | object | yes | The data source to attach to the project. | | source.type | string | yes | Source type. Currently only 'csv' is supported. | | source.id | string | yes | asset:// URI for the data source (see Assets endpoint). | | use_credits | boolean | no | When true, and the data source exceeds the project row limit, charge credits to expand the limit rather than rejecting the request. Credits are charged only for rows above the current limit at the rate of 1 credit per 10 rows. The request is rejected with 422 if the account has insufficient credits. | ## Response `201 Created` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | — | | name | string | — | | type | string | — | | is_primary | boolean | — | | created_at | string | — | | updated_at | string | — | ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | | 404 | Not Found | | 409 | Conflict | | 422 | Unprocessable Entity | | 500 | Internal Server Error | --- # Get project data source `GET` `/api/v1/carve/projects/{project_id}/data-sources/{data_source_id}` Return a single data source attached to the given project. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | | data_source_id | string | yes | — | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | — | | name | string | — | | type | string | — | | is_primary | boolean | — | | created_at | string | — | | updated_at | string | — | ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | | 404 | Not Found | --- # Replace project data source `PUT` `/api/v1/carve/projects/{project_id}/data-sources/{data_source_id}` Replace an existing attached data source with a new one of the same type. The replacement inherits the primary/non-primary status of the data source it replaces. Set `use_credits` to true to allow the replacement to exceed the row limit by charging credits for the overage (1 credit per 10 rows above the limit) when replacing a primary data source. Returns 422 if the account has insufficient credits. The account sheet will need to be refreshed to reflect any changes. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | | data_source_id | string | yes | — | ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | source | object | yes | The replacement data source. Must be the same type as the existing data source. | | source.type | string | yes | Source type. Currently only 'csv' is supported. | | source.id | string | yes | asset:// URI for the data source (see Assets endpoint). | | use_credits | boolean | no | When true, and the data source exceeds the project row limit, charge credits to expand the limit rather than rejecting the request. Credits are charged only for rows above the current limit at the rate of 1 credit per 10 rows. The request is rejected with 422 if the account has insufficient credits. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | — | | name | string | — | | type | string | — | | is_primary | boolean | — | | created_at | string | — | | updated_at | string | — | ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 500 | Internal Server Error | --- # Remove project data source `DELETE` `/api/v1/carve/projects/{project_id}/data-sources/{data_source_id}` Detach a data source from the project entirely. This cannot be undone. The account sheet will need to be refreshed to reflect any changes. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | | data_source_id | string | yes | — | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | data_source_id | string | — | | project_id | string | — | ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 500 | Internal Server Error | --- # Download project data source `GET` `/api/v1/carve/projects/{project_id}/data-sources/{data_source_id}/download` Return a short-lived S3 presigned URL for the processed data file backing the given project data source. The URL expires in one hour (`expires_in` seconds). ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | | data_source_id | string | yes | — | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | download_url | string | — | | expires_in | integer | — | ## Errors | Status | Description | |--------|-------------| | 404 | Not Found | --- # List project scenarios `GET` `/api/v1/carve/projects/{project_id}/scenarios` Return scenarios for the given project, ordered by creation date descending. Scenarios are cached locally and reflect the last time project assets were fetched. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | — | | name | string | — | | is_executed | boolean | — | | created_at | string | — | | description | string | Short human-readable summary of the scenario. | | description_is_user_set | boolean | True if the description was explicitly set by a user. | ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | | 404 | Not Found | --- # Create project scenario `POST` `/api/v1/carve/projects/{project_id}/scenarios` Start a fresh scenario with the Carve agent ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | name | string | yes | Scenario name. | | description | string | no | Short and concise scenario description | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | scenario_id | string | — | | project_id | string | — | | message | string | — | | intro_message | string | The Carve agent's opening message for the new scenario. | | carve_status | string | The new scenario's carve status. | ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | | 404 | Not Found | | 409 | Conflict | | 422 | Unprocessable Entity | | 500 | Internal Server Error | | 502 | Bad Gateway | --- # Get project scenario `GET` `/api/v1/carve/projects/{project_id}/scenarios/{scenario_id}` Return a single scenario by ID. The scenario must belong to the given project. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | | scenario_id | string | yes | — | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | — | | name | string | — | | is_executed | boolean | — | | created_at | string | — | | description | string | Short human-readable summary of the scenario. | | description_is_user_set | boolean | True if the description was explicitly set by a user. | ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | | 404 | Not Found | --- # Rename project scenario `PUT` `/api/v1/carve/projects/{project_id}/scenarios/{scenario_id}` Update the display name of a scenario. Both the scenario record and any result column headers in the downloaded CSV are updated atomically. Renaming a scenario that has been executed does not affect its results or conversation history. Returns 400 if a scenario with the requested name already exists in the project. If the project is currently open in the browser, refresh the page to see the updated name. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | | scenario_id | string | yes | — | ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | name | string | yes | New name for the scenario. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | scenario_id | string | — | | project_id | string | — | | name | string | — | | message | string | — | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 502 | Bad Gateway | --- # Delete project scenario `DELETE` `/api/v1/carve/projects/{project_id}/scenarios/{scenario_id}` Permanently remove a scenario from the project. The scenario's results, conversation history, and overrides are deleted and cannot be recovered. Returns 409 if the scenario has a deploy job currently in progress. If the project is currently open in the browser, refresh the page to see the scenario removed. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | | scenario_id | string | yes | — | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | scenario_id | string | — | | project_id | string | — | | message | string | — | ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | | 404 | Not Found | | 409 | Conflict | | 502 | Bad Gateway | --- # Get scenario account sheet `GET` `/api/v1/carve/projects/{project_id}/scenarios/{scenario_id}/account-sheet` Fetch the scenario account sheet from Bookcarver, upload a CSV export to S3, and return a presigned download URL. The account sheet includes the scenario's results as two additional columns named `{Scenario Name} (Results)` and `{Scenario Name} (Rationale)`. The project must belong to the authenticated account. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | | scenario_id | string | yes | — | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | download_url | string | — | | expires_in | integer | — | ## Errors | Status | Description | |--------|-------------| | 404 | Not Found | --- # Get scenario account sheet schema `GET` `/api/v1/carve/projects/{project_id}/scenarios/{scenario_id}/account-sheet/schema` Returns the field names, types, and primary key for the account sheet associated with the given scenario, without downloading all row data. The scenario must belong to the authenticated account and must be executed. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | | scenario_id | string | yes | — | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | fields | array[object] | — | | fields.name | string | — | | fields.type | string | — | | primary_key | array[string] | — | ## Errors | Status | Description | |--------|-------------| | 404 | Not Found | | 409 | Conflict | | 502 | Bad Gateway | --- # Reset scenario account sheet `POST` `/api/v1/carve/projects/{project_id}/scenarios/{scenario_id}/account-sheet/reset` Updates the scenario so it runs against the current account sheet. Any existing carve results for this scenario will no longer be accessible. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | | scenario_id | string | yes | — | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | project_id | string | — | | scenario_id | string | — | ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | | 404 | Not Found | | 502 | Bad Gateway | --- # Get account sheet refresh preview `GET` `/api/v1/carve/projects/{project_id}/account-sheet/refresh-preview` Returns the column names that would be added, updated, or removed if the account sheet were refreshed against the latest data from connected sources. The project must belong to the authenticated account. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | removed | array[string] | — | | added | array[string] | — | | updated | array[string] | — | ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | | 404 | Not Found | | 502 | Bad Gateway | --- # Refresh account sheet `POST` `/api/v1/carve/projects/{project_id}/account-sheet/refresh` Refreshes the account sheet using the latest data from connected sources, creating a new version with any added, updated, or removed columns. The project must belong to the authenticated account. After refreshing, existing scenarios will continue using the previous account sheet version until the user chooses to update them via `reset_carve_project_scenario_account_sheet`. Note that resetting a scenario will make any existing carve results for that scenario inaccessible. Call `get_carve_project_account_sheet_refresh_preview` first to show the user which columns will change before triggering the refresh. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | project_id | string | — | ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | | 404 | Not Found | | 502 | Bad Gateway | --- # Set scenario account sheet overrides `PATCH` `/api/v1/carve/projects/{project_id}/scenarios/{scenario_id}/account-sheet/overrides` Override or clear the assigned result and rationale for one or more rows in an executed scenario account sheet. All changes are atomic: if any row_key is unknown, the entire request is rejected and no override changes are written. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | | scenario_id | string | yes | — | ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | overrides | array[object] | yes | — | | overrides.row_key | integer | yes | gw_row_number of the row to override | | overrides.result | string | yes | New result value. Send null to clear this row's override. Empty string is rejected — use null to clear. | | overrides.rationale | string | no | Optional rationale. Ignored when result is null. Auto-generated if omitted or null and result is a string. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | project_id | string | — | | scenario_id | string | — | | changes | array[object] | — | | changes.row_key | integer | — | | changes.prior_result | string | — | | changes.prior_rationale | string | — | | changes.result | string | — | | changes.rationale | string | — | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 409 | Conflict | | 502 | Bad Gateway | --- # Clone project scenario `POST` `/api/v1/carve/projects/{project_id}/scenarios/{scenario_id}/clone` Create a new scenario by copying an existing one, including its results, overrides, and conversation history. The clone is an independent snapshot — subsequent changes to either scenario do not affect the other. If `name` is omitted, the new scenario is named '{source name} (copy)'. If the project is currently open in the browser, refresh the page to see the new scenario. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | | scenario_id | string | yes | — | ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | name | string | no | Name for the cloned scenario. Defaults to '{source name} (copy)' if omitted. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | scenario_id | string | — | | project_id | string | — | | message | string | — | ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | | 404 | Not Found | | 409 | Conflict | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Update project scenario description `PATCH` `/api/v1/carve/projects/{project_id}/scenarios/{scenario_id}/description` Set or clear the human-readable description for a scenario. Pass null to clear it. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | | scenario_id | string | yes | — | ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | description | string | no | New description. Pass null to clear it. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | scenario_id | string | — | | project_id | string | — | | description | string | — | | message | string | — | ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Deploy project salesforce `POST` `/api/v1/carve/projects/{project_id}/scenarios/{scenario_id}/deploy-salesforce` Trigger an export of a Carve scenario's results to Salesforce. The `crm_connection_id` must identify an active Salesforce OAuth connection. Returns a job ID that can be polled via `GET //deploy/`. To discover available account sheet columns for `field_mappings.account_sheet_column`, call `GET //scenarios//account-sheet/schema` first. To discover valid Salesforce field names for `field_mappings` and `additional_field_updates`, call `GET /api/v1/crm-connections/{crm_connection_id}/metadata` first. Use the `value` from each field object as `match_field`, `update_field`, or `additional_field_updates[].field`. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | | scenario_id | string | yes | — | ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | crm_connection_id | string | yes | — | | field_mappings | object | yes | — | | field_mappings.account_sheet_column | string | yes | Column name from the scenario account sheet used to match account sheet rows to CRM records (e.g. the account ID or a unique identifier field). Use `GET //scenarios//account-sheet/schema` to discover available column names. | | field_mappings.match_field | string | yes | Salesforce field used to match account sheet rows to CRM records (e.g. the account ID or a unique identifier field). Use `GET /api/v1/crm-connections/{crm_connection_id}/metadata` to discover valid field names. | | field_mappings.update_field | string | yes | Salesforce field to write the account sheet column value into on each matched record. Must differ from any field listed in `additional_field_updates`. For lookup fields, records are matched using the Name field on the referenced object (e.g. User Name). Duplicate or unmatched records are skipped. | | create_target_books | boolean | no | If true, create Gradient Works target books from the export results. | | target_book_ownership_field | string | no | Salesforce field used to assign ownership when creating target books. | | additional_field_updates | array[object] | no | — | | additional_field_updates.field | string | yes | Salesforce field name to update. Must differ from `field_mappings.update_field`. | | additional_field_updates.value | string | yes | Value to write into the Salesforce field. String values must be non-empty. | ## Response `202 Accepted` — `application/json` | Field | Type | Description | |-------|------|-------------| | job_id | string | — | | status | string | — | ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | | 404 | Not Found | | 409 | Conflict | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Deploy project hubspot `POST` `/api/v1/carve/projects/{project_id}/scenarios/{scenario_id}/deploy-hubspot` Trigger an export of a Carve scenario's results to HubSpot. The `crm_connection_id` must identify an active HubSpot OAuth connection. Returns a job ID that can be polled via `GET //deploy/`. To discover available account sheet columns for `property_mappings.account_sheet_column`, call `GET //scenarios//account-sheet/schema` first. To discover valid HubSpot property names for `property_mappings` and `additional_property_updates`, call `GET /api/v1/crm-connections/{crm_connection_id}/metadata` first. Use the `value` from each property object as `match_property`, `update_property`, or `additional_property_updates[].property`. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | | scenario_id | string | yes | — | ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | crm_connection_id | string | yes | — | | property_mappings | object | yes | — | | property_mappings.account_sheet_column | string | yes | Column name from the scenario account sheet used to match account sheet rows to CRM records (e.g. the account ID or a unique identifier field). Use `GET //scenarios//account-sheet/schema` to discover available column names. | | property_mappings.match_property | string | yes | HubSpot property used to match account sheet rows to CRM records (e.g. the account ID or a unique identifier property). Use `GET /api/v1/crm-connections/{crm_connection_id}/metadata` to discover valid property names. | | property_mappings.update_property | string | yes | HubSpot property to write the account sheet column value into on each matched record. Must differ from any property listed in `additional_property_updates`. For lookup properties, records are matched using the Name field on the referenced object (e.g. User Name). Duplicate or unmatched records are skipped. | | additional_property_updates | array[object] | no | — | | additional_property_updates.property | string | yes | HubSpot property name to update. Must differ from `property_mappings.update_property`. | | additional_property_updates.value | string | yes | Value to write into the HubSpot property. String values must be non-empty. | ## Response `202 Accepted` — `application/json` | Field | Type | Description | |-------|------|-------------| | job_id | string | — | | status | string | — | ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | | 404 | Not Found | | 409 | Conflict | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Get deploy job `GET` `/api/v1/carve/projects/{project_id}/deploy/{job_id}` Poll the status of a deploy job created by `POST //deploy`. `results` is `null` while the job is pending or processing; once complete or failed it contains `success_count` and `total_count`. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | project_id | string | yes | — | | job_id | string | yes | — | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | job_id | string | — | | status | string | — | | export_name | string | — | | scenario_id | string | — | | created_at | string | — | | updated_at | string | — | | results | object | — | | results.success_count | integer | — | | results.total_count | integer | — | ## Errors | Status | Description | |--------|-------------| | 403 | Forbidden | | 404 | Not Found | --- ### Assignments # List assignments `GET` `/api/v1/crm-connections/{crm_connection_id}/assignments` Return assignment records for the given Salesforce CRM connection. Supports filtering by date range, assignment type, assigned rep, record, distribution run, retrieval run, and queue. Supports sorting and cursor-based pagination. Default page size is 50. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | query | string | no | Full-text search across record name, queue name, flow name, and detail. Minimum 2 characters. | | start_date | string | no | Filter records with assigned_time on or after this date (YYYY-MM-DD) | | end_date | string | no | Filter records with assigned_time on or before this date (YYYY-MM-DD) | | assignment_type | string | no | Filter by assignment type: Queue, Direct, or Other | | assigned_to | string | no | Filter by Salesforce User ID of the assigned rep | | record_id | string | no | Filter by Salesforce ID of the assigned record | | record_name | string | no | Filter by exact name of the assigned record | | distribution_run_id | string | no | Filter by Salesforce ID of the distribution run. Mutually exclusive with retrieval_run_id. | | retrieval_run_id | string | no | Filter by Salesforce ID of the retrieval run. Mutually exclusive with distribution_run_id. | | queue_id | string | no | Filter by Salesforce ID of the Gradient Works queue | | sort_by | string | no | Field to sort by: assigned_at (default), assignment_type, record_name, new_owner_name, previous_owner_name, created_at, or updated_at | | sort_direction | string | no | Sort direction: DESC (default) or ASC | | limit | string | no | Maximum number of records to return (default: 50) | | include_book_matches | boolean | no | When true, each assignment includes a book_definition_matches array listing the target books and segments that matched. Only populated for distribution assignments; null for retrievals and manual or queue assignments. | | next_records_url | string | no | Cursor returned by a previous response to fetch the next page. When provided, all other query params are ignored. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | assignments | array[object] | Assignment records | | assignments.id | string | Salesforce ID of the assignment record | | assignments.name | string | Record name | | assignments.created_at | string | ISO-8601 creation timestamp | | assignments.updated_at | string | ISO-8601 last-modified timestamp | | assignments.assignment_type | string | Assignment type: Queue, Direct, or Other | | assignments.assigned_time | string | ISO-8601 timestamp when the assignment was made | | assignments.assigned_by | object | User who triggered the assignment | | assignments.assigned_by.id | string | Salesforce User ID | | assignments.assigned_by.name | string | User display name | | assignments.record | object | The CRM record that was assigned | | assignments.record.id | string | Salesforce ID of the assigned record | | assignments.record.type | string | Salesforce object type (Account, Contact, Lead, Opportunity) | | assignments.record.field | string | Salesforce field updated by the assignment | | assignments.record.name | string | Name of the assigned record | | assignments.new_owner | object | User the record was assigned to | | assignments.new_owner.id | string | Salesforce User ID | | assignments.new_owner.name | string | User display name | | assignments.previous_owner | object | Previous owner (null if record had no prior owner) | | assignments.previous_owner.id | string | Salesforce User ID | | assignments.previous_owner.name | string | User display name | | assignments.queue | object | Queue context (null for non-Queue assignments) | | assignments.queue.id | string | Salesforce Queue ID | | assignments.queue.name | string | Queue name | | assignments.queue.item_id | string | Salesforce ID of the associated Gradient Works queue item | | assignments.request_id | string | Request ID associated with this assignment | | assignments.flow | object | Flow context (null when the assignment was not triggered by a Salesforce Flow) | | assignments.flow.name | string | Name of the flow that ran | | assignments.flow.version | number | Version of the flow that ran | | assignments.flow.version_durable_id | string | Durable ID of the flow version | | assignments.flow.interview_guid | string | GUID of the flow interview | | assignments.distribution | object | Distribution run that produced this assignment (null if not from a distribution) | | assignments.distribution.id | string | Salesforce ID of the run | | assignments.distribution.name | string | Name of the distribution or retrieval definition | | assignments.retrieval | object | Retrieval run that produced this assignment (null if not from a retrieval) | | assignments.retrieval.id | string | Salesforce ID of the run | | assignments.retrieval.name | string | Name of the distribution or retrieval definition | | assignments.detail | string | Additional assignment detail | | assignments.book_definition_matches | array[object] | Target books and segments matched for this assignment. Only populated for distribution assignments when include_book_matches=true; null for retrievals and manual or queue assignments. | | next_records_url | string | Pass this value as next_records_url on the next request to fetch the following page. Null when all records have been returned. | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- ### Log Entries # List log entries `GET` `/api/v1/crm-connections/{crm_connection_id}/log-entries` Returns Gradient Works log entries for the given Salesforce CRM connection. Supports filtering by date range and text search across logger, flow name, request ID, and flow interview GUID fields. Default page size is 50; results are ordered by timestamp descending. Pass next_records_url from one response into the next request to paginate through large result sets. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | query | string | no | Search across log entry text fields: logger, flow name, request ID, and flow interview GUID. Requires at least 2 characters. | | start_date | string | no | Return log entries on or after this date (inclusive), formatted as YYYY-MM-DD. Filters on the log timestamp field. | | end_date | string | no | Return log entries on or before this date (inclusive), formatted as YYYY-MM-DD. Filters on the log timestamp field. | | sort_by | string | no | Field to sort by. Default: GradientWorks__Timestamp__c. | | sort_direction | string | no | ASC or DESC | | limit | string | no | Maximum number of log entries to return (default: 50). | | next_records_url | string | no | Cursor returned by a previous response to fetch the next page. When provided, all other query params are ignored. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | log_entries | array[object] | — | | log_entries.id | string | Salesforce ID of the log entry | | log_entries.name | string | Log entry record name | | log_entries.level | string | Log level (e.g. ERROR, WARN, INFO, DEBUG) | | log_entries.level_number | number | Numeric log level | | log_entries.message | string | Log message | | log_entries.logger | string | Logger name | | log_entries.context | string | Additional context | | log_entries.request_id | string | Request ID | | log_entries.flow | object | Flow context (null when the log entry was not recorded from a Salesforce Flow) | | log_entries.flow.name | string | Name of the flow that ran | | log_entries.flow.version | number | Version of the flow that ran | | log_entries.flow.version_durable_id | string | Durable ID of the flow version | | log_entries.flow.interview_guid | string | GUID of the flow interview | | log_entries.timestamp | string | Log timestamp (ISO-8601) | | log_entries.timestamp_milliseconds | number | Log timestamp in milliseconds since epoch | | log_entries.created_at | string | ISO-8601 creation timestamp | | next_records_url | string | Pass this value as next_records_url on the next request to fetch the following page. Null when all records have been returned. Only present when limit exceeds Salesforce's per-page maximum (2000 records). | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- ### Flows # List flows `GET` `/api/v1/crm-connections/{crm_connection_id}/flows` Return flow definitions from the connected Salesforce org. Optionally search by label. Supports sorting and cursor-based pagination. Default page size is 50. Pass next_records_url from one response into the next request to paginate through large result sets. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | query | string | no | Search by flow label (min 2 characters) | | trigger_object | string | no | Filter by trigger object or event label (exact match, e.g. Opportunity, Account). | | process_type | string | no | Filter by flow process type (exact match, e.g. Flow, AutoLaunchedFlow, Workflow). | | is_active | string | no | Filter by active status. true returns only flows with an active version; false returns only flows with no active version. Omit to return all flows regardless of active status. | | is_template | string | no | Filter by template status. true returns only template flows; false returns only non-template flows. Omit to return all flows regardless of template status. | | sort_by | string | no | Field to sort by (default: Label) | | sort_direction | string | no | Sort direction: ASC (default) or DESC | | limit | string | no | Maximum number of flows to return (default: 50). | | next_records_url | string | no | Cursor returned by a previous response to fetch the next page. When provided, all other query params are ignored. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | flows | array[object] | List of flow definition records | | flows.id | string | Flow definition ID | | flows.api_name | string | API name of the flow | | flows.label | string | Display label of the flow | | flows.description | string | Description of the flow | | flows.active_version_id | string | ID of the active flow version, or null if there is no active version | | flows.latest_version_id | string | ID of the most recently saved flow version | | flows.version_number | integer | Version number of the latest flow version | | flows.is_active | boolean | Whether the flow has an active version | | flows.is_out_of_date | boolean | Whether the active version is older than the most recently saved version | | flows.is_template | boolean | Whether the flow is a template | | flows.is_overridable | boolean | Whether the flow can be overridden by a subscriber org | | flows.is_swing_flow | boolean | Whether the flow was built with the legacy Swing Designer | | flows.process_type | string | Type of flow (e.g. Flow, AutoLaunchedFlow, Workflow, InvocableProcess) | | flows.record_trigger_type | string | Trigger condition for record-triggered flows (e.g. Create, Update, CreateAndUpdate, Delete) | | flows.trigger_type | string | Trigger type for the flow (e.g. RecordBeforeSave, RecordAfterSave, Scheduled, PlatformEvent) | | flows.trigger_object_or_event_id | string | ID of the object or platform event that triggers the flow | | flows.trigger_object_or_event_label | string | Label of the object or platform event that triggers the flow | | flows.trigger_order | integer | Execution order relative to other record-triggered flows on the same object and trigger event | | flows.builder | string | Tool used to create the flow (e.g. Flow Builder, Cloud Flow Designer) | | flows.api_version | number | API version the flow was saved with | | flows.last_modified_by | string | Name of the user who last modified the flow | | flows.installed_package_name | string | Name of the managed package that installed this flow, if any | | flows.manageable_state | string | Managed package state of the package component. One of: Active, Deleted, Deprecated, DeprecatedEditable, Installed, InstalledEditable, Released, Unmanaged | | flows.namespace_prefix | string | Namespace prefix of the component's package, if any | | flows.durable_id | string | Stable identifier for the flow definition across versions | | flows.source_template_id | string | ID of the template this flow was created from, if any | | flows.overridden_by_id | string | ID of the flow that overrides this one in a subscriber org, if any | | flows.overridden_flow_id | string | ID of the flow being overridden by this one, if any | | next_records_url | string | Pass this value as next_records_url on the next request to fetch the following page. Null when all records have been returned. Only present when limit exceeds Salesforce's per-page maximum (2000 records). | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Create flow `POST` `/api/v1/crm-connections/{crm_connection_id}/flows` Create a new flow in the connected Salesforce org. The new flow is created with Draft status. Returns the DurableId of the created flow. The API name must be unique across the org. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | full_name | string | yes | Unique API name for the flow. Must start with a letter and contain only letters, digits, and underscores. Must not end with an underscore or contain consecutive underscores. | | label | string | yes | Display label for the flow. | | process_type | string | yes | Type of flow to create (e.g. AutoLaunchedFlow, Flow). Determines the flow's runtime behavior and available elements. | | description | string | no | Description of the flow. | | api_version | number | no | API version for the flow's execution behavior. Defaults to the org's current API version if omitted. | | metadata | object | no | Additional flow metadata fields (e.g. elements, variables, formulas). Merged into the base metadata sent to Salesforce. | ## Response `201 Created` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | DurableId of the newly created flow definition | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 409 | Conflict | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Get flow `GET` `/api/v1/crm-connections/{crm_connection_id}/flows/{flow_id}` Return the flow definition for a single flow by its DurableId. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | flow_id | string | yes | DurableId of the flow definition | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | Flow definition ID | | api_name | string | API name of the flow | | label | string | Display label of the flow | | description | string | Description of the flow | | active_version_id | string | ID of the active flow version, or null if there is no active version | | latest_version_id | string | ID of the most recently saved flow version | | version_number | integer | Version number of the latest flow version | | is_active | boolean | Whether the flow has an active version | | is_out_of_date | boolean | Whether the active version is older than the most recently saved version | | is_template | boolean | Whether the flow is a template | | is_overridable | boolean | Whether the flow can be overridden by a subscriber org | | is_swing_flow | boolean | Whether the flow was built with the legacy Swing Designer | | process_type | string | Type of flow (e.g. Flow, AutoLaunchedFlow, Workflow, InvocableProcess) | | record_trigger_type | string | Trigger condition for record-triggered flows (e.g. Create, Update, CreateAndUpdate, Delete) | | trigger_type | string | Trigger type for the flow (e.g. RecordBeforeSave, RecordAfterSave, Scheduled, PlatformEvent) | | trigger_object_or_event_id | string | ID of the object or platform event that triggers the flow | | trigger_object_or_event_label | string | Label of the object or platform event that triggers the flow | | trigger_order | integer | Execution order relative to other record-triggered flows on the same object and trigger event | | builder | string | Tool used to create the flow (e.g. Flow Builder, Cloud Flow Designer) | | api_version | number | API version the flow was saved with | | last_modified_by | string | Name of the user who last modified the flow | | installed_package_name | string | Name of the managed package that installed this flow, if any | | manageable_state | string | Managed package state of the package component. One of: Active, Deleted, Deprecated, DeprecatedEditable, Installed, InstalledEditable, Released, Unmanaged | | namespace_prefix | string | Namespace prefix of the component's package, if any | | durable_id | string | Stable identifier for the flow definition across versions | | source_template_id | string | ID of the template this flow was created from, if any | | overridden_by_id | string | ID of the flow that overrides this one in a subscriber org, if any | | overridden_flow_id | string | ID of the flow being overridden by this one, if any | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # List flow versions `GET` `/api/v1/crm-connections/{crm_connection_id}/flows/{flow_id}/versions` Return all versions of a flow definition, ordered by version number descending. Each version includes its status (Active, Draft, Obsolete, or InvalidDraft) and version number. Pass next_records_url from one response into the next request to paginate through large result sets. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | flow_id | string | yes | DurableId of the flow definition | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | next_records_url | string | no | Cursor returned by a previous response to fetch the next page. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | versions | array[object] | List of flow versions, ordered by version number descending | | versions.id | string | Salesforce Tooling API record ID. Not usable as a version identifier — use durable_id instead. | | versions.durable_id | string | Stable version identifier (DurableId). Use this as the version_id in get_flow_version, update_flow_version, activate_flow_version, and deactivate_flow_version. | | versions.flow_definition_view_id | string | DurableId of the parent flow definition | | versions.label | string | Display label of the flow version | | versions.description | string | Description of the flow version | | versions.status | string | Version status: Active, Draft, Obsolete, InvalidDraft, or UnderReview | | versions.version_number | integer | Version number | | versions.process_type | string | Type of flow (e.g. Flow, AutoLaunchedFlow, Workflow, InvocableProcess) | | versions.api_version | number | API version the flow was saved with | | versions.api_version_runtime | number | API version used for running the flow. Determines which versioned run-time behavior improvements are adopted. | | versions.run_in_mode | string | Run mode for the flow: DefaultMode, SystemModeWithSharing, or SystemModeWithoutSharing | | versions.is_template | boolean | Whether the flow is a template | | versions.is_swing_flow | boolean | Whether the flow was built with Desktop Flow Designer | | next_records_url | string | Pass this value as next_records_url on the next request to fetch the following page. Null when all records have been returned. | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Create flow version `POST` `/api/v1/crm-connections/{crm_connection_id}/flows/{flow_id}/versions` Create a new version of an existing flow in the connected Salesforce org. Use get_flow_version to fetch an existing version's metadata as a starting point, then modify and pass it here. Returns the id (DurableId) of the newly created version. To update an existing draft version use update_flow_version. To activate or deactivate a version use activate_flow_version or deactivate_flow_version. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | flow_id | string | yes | DurableId of the flow definition | ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | metadata | object | yes | Complete flow metadata object. Fetch the current metadata via get_flow_version first, modify what you need, then submit the full object — not just the changed fields. | ## Response `201 Created` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | DurableId of the newly created flow version | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Get flow version `GET` `/api/v1/crm-connections/{crm_connection_id}/flows/{flow_id}/versions/{version_id}` Return the full configuration for a specific flow version, including its Metadata JSON. By default, null fields and empty lists are stripped from the metadata response. Set simplified=false to receive the unprocessed metadata. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | flow_id | string | yes | DurableId of the flow definition | | version_id | string | yes | DurableId of the flow version, as returned in the durable_id field of list_flow_versions | ## Query parameters | Name | Type | Required | Description | |------|------|----------|-------------| | simplified | boolean | no | When true (default), null fields and empty lists are stripped from the metadata response, producing a leaner representation. Set to false to receive the unprocessed metadata. | ## Response `200 OK` — `application/json` | Field | Type | Description | |-------|------|-------------| | id | string | Flow version record ID | | api_version | number | API version that defines the execution behavior of the flow | | definition_id | string | ID of the parent FlowDefinition record | | description | string | Description of the flow version | | environments | string | Where the flow can run (e.g. Default, Offline, Slack). Multipicklist value. | | full_name | string | Fully qualified API name of the flow version (e.g. MyFlow-3) | | is_template | boolean | Whether the flow is a template | | manageable_state | string | Managed package state of the component. One of: beta, deleted, deprecated, deprecatedEditable, installed, installedEditable, released, unmanaged | | master_label | string | Display label for the flow version | | metadata | string | Full flow version configuration. Null for flows in managed packages (unless they are templates). | | process_type | string | Type of flow (e.g. Flow, AutoLaunchedFlow, Workflow, InvocableProcess) | | run_in_mode | string | Run mode: DefaultMode, SystemModeWithSharing, or SystemModeWithoutSharing | | status | string | Version status: Active, Draft, Obsolete, UnderReview, or InvalidDraft | | time_zone_sid_key | string | Time zone ID in which the flow runs | | version_number | integer | Version number | ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Update flow version `PATCH` `/api/v1/crm-connections/{crm_connection_id}/flows/{flow_id}/versions/{version_id}` Update a flow version by submitting a complete metadata object. The metadata must include all fields, not just the ones being changed — fetch the current metadata via get_flow_version first, modify what you need, then submit the full object. Returns 204 on success. Active versions cannot be edited. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | flow_id | string | yes | DurableId of the flow definition | | version_id | string | yes | DurableId of the flow version, as returned in the durable_id field of list_flow_versions | ## Request body `application/json` | Field | Type | Required | Description | |-------|------|----------|-------------| | metadata | object | yes | Complete flow metadata object. Fetch the current metadata via get_flow_version first, modify what you need, then submit the full object — not just the changed fields. | ## Response `204 No Content` — `application/json` _No response body._ ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Activate flow version `POST` `/api/v1/crm-connections/{crm_connection_id}/flows/{flow_id}/versions/{version_id}/activate` Sets this version as the active version of the flow. The previously active version (if any) becomes Obsolete. Returns 204 on success. If the version is already Active, returns 204 without making changes. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | flow_id | string | yes | DurableId of the flow definition | | version_id | string | yes | DurableId of the flow version, as returned in the durable_id field of list_flow_versions | ## Response `204 No Content` — `application/json` _No response body._ ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- # Deactivate flow version `POST` `/api/v1/crm-connections/{crm_connection_id}/flows/{flow_id}/deactivate` Clears the active version of a flow, transitioning it to Obsolete. Returns 204 on success. If the flow has no active version, returns 204 without making changes. ## Path parameters | Name | Type | Required | Description | |------|------|----------|-------------| | crm_connection_id | string | yes | UUID of the CRM connection | | flow_id | string | yes | DurableId of the flow definition | ## Response `204 No Content` — `application/json` _No response body._ ## Errors | Status | Description | |--------|-------------| | 400 | Bad Request | | 403 | Forbidden | | 404 | Not Found | | 422 | Unprocessable Entity | | 502 | Bad Gateway | --- ## MCP | Tool Name | What it does | |-----------|--------------| | `list_crm_connections` | List CRM connections. | | `list_crm_connection_metadata` | List CRM object field metadata for a connection. | | `upload_asset` | Get a pre-signed upload URL. | | `complete_asset_upload` | Complete a file upload. | | `list_market_maps` | List market maps. | | `create_market_map` | Create a market map. | | `get_market_map` | Get a market map. | | `list_market_map_runs` | List runs for a market map. | | `get_current_market_map_run` | Get the latest run. | | `get_market_map_run` | Get a single run. | | `get_market_map_run_progress` | Poll run progress. | | `get_market_map_run_results` | Get run results. | | `cancel_market_map_run` | Cancel a run. | | `list_market_map_accounts` | List all accounts in a market map. | | `add_accounts_to_market_map` | Add accounts to an existing market map. | | `get_market_map_summary` | Get market map summary metrics. | | `list_market_map_clusters` | List clusters in a market map. | | `get_market_map_cluster` | Get cluster detail. | | `export_market_map_csv` | Export market map as CSV. | | `get_market_map_account` | Get full account detail. | | `get_market_map_similar_accounts` | Get similar reference accounts. | | `list_target_books` | List target books. | | `create_target_book` | Create a target book. | | `get_target_book` | Get a target book. | | `update_target_book` | Update target book fields. | | `delete_target_book` | Delete a target book. | | `add_reps_to_target_book` | Add reps to a target book. | | `assign_reps_to_target_book` | Replace all reps on a target book. | | `remove_reps_from_target_book` | Remove one or more reps from a target book. | | `add_target_book_segment` | Add a new segment to a target book. | | `replace_target_book_segments` | Replace all segments on a target book. | | `update_target_book_segment` | Update a target book segment. | | `delete_target_book_segment` | Delete a segment from a target book. | | `list_reps` | List bookbuilder reps. | | `get_rep` | Get a bookbuilder rep. | | `list_rep_accounts` | List a rep's accounts. | | `list_rep_opportunities` | List a rep's opportunities. | | `get_rep_activities_by_day_chart` | Get a rep's completed-activity counts bucketed by day. | | `get_rep_created_opps_by_day_chart` | Get a rep's created-opportunity counts bucketed by day. | | `list_distribution_templates` | List distribution templates. | | `get_distribution` | Get a distribution. | | `list_distribution_runs` | List distribution runs. | | `get_distribution_run` | Get distribution run detail. | | `list_distribution_run_assignments` | List assignments for a distribution run. | | `list_queues` | List queues. | | `get_queue` | Get a queue. | | `list_retrieval_templates` | List retrieval templates. | | `get_retrieval` | Get a retrieval. | | `list_retrieval_runs` | List retrieval runs. | | `get_retrieval_run` | Get retrieval run detail. | | `list_retrieval_run_assignments` | List assignments for a retrieval run. | | `list_carve_projects` | List projects. | | `create_carve_project` | Create a project. | | `get_carve_project` | Get a project. | | `list_carve_project_data_sources` | List data sources for a project. | | `attach_carve_project_data_source` | Attach a data source to a project. | | `get_carve_project_data_source` | Get a data source for a project. | | `replace_carve_project_data_source` | Replace a data source on a project. | | `remove_carve_project_data_source` | Remove a data source from a project. | | `download_carve_project_data_source` | Get a presigned download URL for a data source file. | | `list_carve_project_scenarios` | List scenarios for a project. | | `create_carve_project_scenario` | Create a Carve scenario. | | `get_carve_project_scenario` | Get a scenario. | | `rename_carve_project_scenario` | Rename a Carve scenario. | | `delete_carve_project_scenario` | Delete a Carve scenario. | | `get_carve_project_scenario_account_sheet` | Get the account sheet for a scenario. | | `get_carve_project_scenario_account_sheet_schema` | Get the schema of a scenario account sheet. | | `reset_carve_project_scenario_account_sheet` | Refresh a scenario to use the latest account sheet version. | | `get_carve_project_account_sheet_refresh_preview` | Preview changes that would result from refreshing the account sheet. | | `refresh_carve_project_account_sheet` | Refresh the account sheet for a project. | | `set_carve_project_scenario_account_sheet_overrides` | Override Results and Rationale for rows in a scenario account sheet. | | `clone_carve_project_scenario` | Clone a Carve scenario. | | `update_carve_project_scenario_description` | Update a Carve scenario description. | | `deploy_carve_project_to_salesforce` | Deploy a Carve scenario to Salesforce. | | `deploy_carve_project_to_hubspot` | Deploy a Carve scenario to HubSpot. | | `get_carve_project_deploy_job` | Get the status of a Carve deploy job. | | `list_assignments` | List assignments. | | `list_log_entries` | List log entries. | | `list_flows` | List flows. | | `create_flow` | Create a flow. | | `get_flow` | Get a flow. | | `list_flow_versions` | List flow versions. | | `create_flow_version` | Create a flow version. | | `get_flow_version` | Get a flow version. | | `update_flow_version` | Update a flow version. | | `activate_flow_version` | Activate a flow version. | | `deactivate_flow_version` | Deactivate a flow. | | `get_scenario_artifacts` | Get scenario artifacts for explaining assignment reasoning. | | `list_carve_project_scenario_cards` | List analysis cards for a Carve scenario. | | `get_carve_project_scenario_card` | Get a Carve scenario analysis card. | | `save_carve_project_scenario_card` | Save a Carve analysis card to a scenario. | | `send_carve_message` | Send a message to a Carve scenario's agent and start a turn. | | `get_carve_turn` | Get a Carve turn's status. | | `get_carve_session` | Get a Carve scenario's session state. | | `get_carve_transcript` | Get a Carve scenario's recent transcript messages. | | `get_carve_project_scenario_card_image_link` | Mint (get-or-create) a Carve scenario card's public image-link URL. | | `view_carve_project_scenario_card_image_link` | Mint a Carve scenario card's public image-link URL and render it inline | | `list_carve_scenario_feedback_threads` | List a scenario's feedback threads with their comments. | | `create_carve_scenario_feedback_thread` | Start a feedback thread with an opening comment. | | `reply_to_carve_scenario_feedback_thread` | Add a comment to a feedback thread. | | `edit_carve_scenario_feedback_comment` | Replace the text of a feedback comment. | | `delete_carve_scenario_feedback_comment` | Delete a feedback comment. | | `resolve_carve_scenario_feedback_thread` | Mark a feedback thread as resolved. | | `reopen_carve_scenario_feedback_thread` | Reopen a resolved feedback thread. | | `who_am_i` | Identify the caller. | ## Common Patterns - All endpoints require Bearer token authentication. - Target book, distribution, retrieval, queue, and rep endpoints require a `crm_connection_id`; call CRM Connections first. - Paginated endpoints use cursor-based pagination via `next_records_url`. - Market map creation and account addition are asynchronous: create the map or account add run, poll run progress, then fetch results when complete. - Market map and CRM connection IDs are UUIDs; target book, segment, and rep IDs are CRM record IDs; cluster IDs are integers.