Opportunities & Pipelines
An opportunity tracks a deal as it moves through a pipeline, from first contact to won or lost. Each opportunity sits in one pipeline stage, can link back to a contact, and carries a monetary value you can report on.
Base URL: https://services.smbcrm.com · Version header: v3 ·
Scopes: opportunities.readonly (read), opportunities.write (create/update/delete
opportunities). Pipeline management uses pipelines.readonly (get one pipeline),
pipelines.create (create), and pipelines.write (update/delete). Listing pipelines only
needs opportunities.readonly. See Scopes.
Pipelines
Section titled “Pipelines”A pipeline is an ordered list of stages that deals move through. You can build pipelines and stages in the SMBcrm UI, or create and manage them with the endpoints in this section.
List pipelines
Section titled “List pipelines”locationId is required as a query parameter. Use this endpoint to look up the pipeline and
stage IDs you need when you create or move an opportunity. Each pipeline’s position is a
string sort key (such as a0V), not a number, and it orders pipelines in your account.
curl "https://services.smbcrm.com/opportunities/pipelines?locationId=<location_id>" \ -H "Authorization: Bearer <token>" \ -H "Version: v3"{ "pipelines": [ { "id": "<pipeline_id>", "name": "Sales Pipeline", "locationId": "<location_id>", "showInFunnel": true, "showInPieChart": true, "useOpportunityProbability": false, "colorRenderMode": "dot", "position": "a0V", "stages": [ { "id": "<pipeline_stage_id>", "name": "New Lead", "position": 1, "showInFunnel": true }, { "id": "<pipeline_stage_id>", "name": "Contacted", "position": 2, "showInFunnel": true }, { "id": "<pipeline_stage_id>", "name": "Proposal Sent", "position": 3, "showInFunnel": true }, { "id": "<pipeline_stage_id>", "name": "Won", "position": 4, "showInFunnel": true } ] } ]}Create a pipeline
Section titled “Create a pipeline”name, stages, and locationId are required. Pipeline names must be unique within a
location (case-insensitive), and stage names must be unique within the pipeline. A successful
call returns HTTP 200 with the new pipeline.
| Body field | Description |
|---|---|
name |
Pipeline name. Required. |
locationId |
Your account/location ID. Required. |
stages |
Array of stage objects, at least one. Required. Each stage takes a name, a position, showInFunnel, and an optional stageWinProbability. |
showInFunnel |
Whether the pipeline appears in the funnel view. |
showInPieChart |
Whether the pipeline appears in the pie chart view. |
useOpportunityProbability |
Turns on stage-level win probability. |
colorRenderMode |
How colors render: dot, bg-tint, or none. |
To use manual win probabilities, set useOpportunityProbability to true and give every
stage a stageWinProbability from 0 to 100. If any stage has no value, SMBcrm calculates
probabilities from each stage’s position instead.
curl -X POST https://services.smbcrm.com/opportunities/pipelines \ -H "Authorization: Bearer <token>" \ -H "Version: v3" \ -H "Content-Type: application/json" \ -d '{ "locationId": "<location_id>", "name": "Sales Pipeline", "stages": [ { "name": "New Lead", "position": 1, "showInFunnel": true }, { "name": "Contacted", "position": 2, "showInFunnel": true }, { "name": "Proposal Sent", "position": 3, "showInFunnel": true }, { "name": "Won", "position": 4, "showInFunnel": true } ], "showInFunnel": true, "showInPieChart": true, "useOpportunityProbability": false, "colorRenderMode": "dot" }'{ "id": "<pipeline_id>", "name": "Sales Pipeline", "locationId": "<location_id>", "showInFunnel": true, "showInPieChart": true, "useOpportunityProbability": false, "colorRenderMode": "dot", "position": "a0V", "stages": [ { "id": "<pipeline_stage_id>", "name": "New Lead", "position": 1, "showInFunnel": true }, { "id": "<pipeline_stage_id>", "name": "Contacted", "position": 2, "showInFunnel": true }, { "id": "<pipeline_stage_id>", "name": "Proposal Sent", "position": 3, "showInFunnel": true }, { "id": "<pipeline_stage_id>", "name": "Won", "position": 4, "showInFunnel": true } ]}Retrieve a pipeline
Section titled “Retrieve a pipeline”This endpoint uses the pipelines.readonly scope. A token with only opportunities.readonly
can list pipelines but can’t fetch one by ID.
curl https://services.smbcrm.com/opportunities/pipelines/<pipeline_id> \ -H "Authorization: Bearer <token>" \ -H "Version: v3"{ "id": "<pipeline_id>", "name": "Sales Pipeline", "locationId": "<location_id>", "showInFunnel": true, "showInPieChart": true, "useOpportunityProbability": false, "colorRenderMode": "dot", "position": "a0V", "stages": [ { "id": "<pipeline_stage_id>", "name": "New Lead", "position": 1, "showInFunnel": true }, { "id": "<pipeline_stage_id>", "name": "Won", "position": 2, "showInFunnel": true } ]}Update a pipeline
Section titled “Update a pipeline”Every body field is optional: name, stages, showInFunnel, showInPieChart,
useOpportunityProbability, and colorRenderMode. The body takes no locationId. Pipeline
and stage names must stay unique (case-insensitive) within the location.
stages replaces the whole list:
- Send an existing stage’s
idto keep it. A stage without anidis created. - You can’t remove every stage in one call.
- Opportunities in a removed stage move to the remaining stage with the lowest position.
curl -X PUT https://services.smbcrm.com/opportunities/pipelines/<pipeline_id> \ -H "Authorization: Bearer <token>" \ -H "Version: v3" \ -H "Content-Type: application/json" \ -d '{ "name": "Sales Pipeline", "stages": [ { "id": "<pipeline_stage_id>", "name": "New Lead", "position": 1 }, { "id": "<pipeline_stage_id>", "name": "Contacted", "position": 2 }, { "name": "Demo Scheduled", "position": 3 }, { "id": "<pipeline_stage_id>", "name": "Won", "position": 4 } ] }'The response is HTTP 200 with the updated pipeline, in the same shape as Retrieve a pipeline.
Delete a pipeline
Section titled “Delete a pipeline”curl -X DELETE https://services.smbcrm.com/opportunities/pipelines/<pipeline_id> \ -H "Authorization: Bearer <token>" \ -H "Version: v3"{ "success": true }success is true when the pipeline was deleted. If the deletion fails, message explains
why.
Create an opportunity
Section titled “Create an opportunity”pipelineId, locationId, name, status, and contactId are required.
pipelineStageId is optional and places the deal in a specific stage; use the IDs from
Pipelines above. monetaryValue is the deal size in your account’s currency.
status accepts open, won, lost, or abandoned.
These optional fields are also accepted:
| Body field | Description |
|---|---|
assignedTo |
ID of the user who owns the deal. See Users. |
forecastExpectedCloseDate |
Expected close date: YYYY-MM-DD, YYYY/MM/DD, MM/DD/YYYY, MM-DD-YYYY, YYYY.MM.DD, MM.DD.YYYY, or an ISO 8601 timestamp. |
forecastProbability |
Forecast win probability, as a number from 0 to 100. |
customFields |
Array of custom field values. Each entry names the field by id (key also works for string values) and carries a fieldValue, which can be a string, an array of strings, or an object. |
For field definitions, see Custom Fields, Values & Tags.
curl -X POST https://services.smbcrm.com/opportunities/ \ -H "Authorization: Bearer <token>" \ -H "Version: v3" \ -H "Content-Type: application/json" \ -d '{ "pipelineId": "<pipeline_id>", "locationId": "<location_id>", "pipelineStageId": "<pipeline_stage_id>", "name": "Website redesign - Acme Co.", "status": "open", "contactId": "<contact_id>", "monetaryValue": 4200, "assignedTo": "<user_id>", "forecastExpectedCloseDate": "2026-11-15", "customFields": [{ "id": "<custom_field_id>", "fieldValue": "Referral" }] }'{ "opportunity": { "id": "<opportunity_id>", "name": "Website redesign - Acme Co.", "pipelineId": "<pipeline_id>", "pipelineStageId": "<pipeline_stage_id>", "locationId": "<location_id>", "status": "open", "contactId": "<contact_id>", "assignedTo": "<user_id>", "monetaryValue": 4200, "forecastExpectedCloseDate": "2026-11-15", "customFields": [{ "id": "<custom_field_id>", "fieldValue": "Referral" }], "createdAt": "2026-07-08T15:04:00.000Z" }}Upsert an opportunity
Section titled “Upsert an opportunity”Pass the id of an existing opportunity to update it. The response’s new field is true
when the call created an opportunity and false when it updated one.
| Body field | Description |
|---|---|
pipelineId |
Pipeline ID. Required. |
locationId |
Your account/location ID. Required. |
followers |
Array of user IDs to add or remove as followers. Required. |
followersActionType |
add or remove: what to do with the users in followers. Required. |
isRemoveAllFollowers |
Set to true to remove every follower. Required. |
id |
ID of the opportunity to update. |
name |
Opportunity name. |
status |
open, won, lost, or abandoned. |
lostReasonId |
Lost reason ID, for deals you set to lost. See Lost reasons. |
pipelineStageId |
Stage to place the deal in. |
monetaryValue |
Deal size in your account’s currency. |
assignedTo |
ID of the user who owns the deal. |
forecastExpectedCloseDate |
Expected close date, in the same formats as Create an opportunity. |
forecastProbability |
Forecast win probability, as a number from 0 to 100. |
curl -X POST https://services.smbcrm.com/opportunities/upsert \ -H "Authorization: Bearer <token>" \ -H "Version: v3" \ -H "Content-Type: application/json" \ -d '{ "id": "<opportunity_id>", "pipelineId": "<pipeline_id>", "locationId": "<location_id>", "pipelineStageId": "<pipeline_stage_id>", "status": "open", "monetaryValue": 6800, "followers": [], "followersActionType": "add", "isRemoveAllFollowers": false }'{ "opportunity": { "id": "<opportunity_id>", "name": "Website redesign - Acme Co.", "pipelineId": "<pipeline_id>", "pipelineStageId": "<pipeline_stage_id>", "locationId": "<location_id>", "status": "open", "contactId": "<contact_id>", "monetaryValue": 6800 }, "new": false}Retrieve an opportunity
Section titled “Retrieve an opportunity”The response includes the deal’s forecast fields, custom fields, followers, and a contact
summary. Unlike the create, update, and search responses, it doesn’t include notes,
tasks, calendarEvents, or effectiveProbability.
curl https://services.smbcrm.com/opportunities/<opportunity_id> \ -H "Authorization: Bearer <token>" \ -H "Version: v3"{ "opportunity": { "id": "<opportunity_id>", "name": "Website redesign - Acme Co.", "pipelineId": "<pipeline_id>", "pipelineStageId": "<pipeline_stage_id>", "locationId": "<location_id>", "status": "open", "contactId": "<contact_id>", "assignedTo": "<user_id>", "source": "public api", "monetaryValue": 4200, "forecastExpectedCloseDate": "2026-11-15", "forecastProbability": 40, "customFields": [{ "id": "<custom_field_id>", "fieldValue": "Referral" }], "followers": ["<user_id>"], "contact": { "id": "<contact_id>", "name": "Jordan Lee", "companyName": "Acme Co.", "email": "jordan@example.com", "phone": "+15125550142", "tags": ["website-lead"] }, "createdAt": "2026-07-08T15:04:00.000Z", "updatedAt": "2026-07-09T09:30:00.000Z", "lastStatusChangeAt": "2026-07-08T15:04:00.000Z", "lastStageChangeAt": "2026-07-08T15:04:00.000Z", "lastActionDate": "2026-07-09T09:30:00.000Z" }}Search opportunities
Section titled “Search opportunities”| Query parameter | Description |
|---|---|
locationId |
Your account/location ID. Required. |
pipelineId |
Limit results to one pipeline. |
pipelineStageId |
Limit results to one stage. |
status |
Filter by status: open, won, lost, abandoned, or all. |
contactId |
Limit results to opportunities linked to one contact. |
assignedTo |
Limit results to opportunities assigned to one user. |
campaignId |
Limit results to one campaign. |
id |
Return only the opportunity with this ID. |
country |
Filter by country, as an ISO 3166-1 alpha-2 code such as US. |
q |
Free-text search query, up to 75 characters. |
date |
Start of a date range, as mm-dd-yyyy. |
endDate |
End of the date range, as mm-dd-yyyy. |
order |
Sort order, such as added_asc, added_desc, name_asc, or name_desc. |
limit |
Results per page. Default 20, maximum 100. |
page |
Page number. Default 1. |
startAfter |
Cursor timestamp in epoch milliseconds, taken from meta.startAfter in the previous response. |
startAfterId |
Cursor ID, taken from meta.startAfterId in the previous response. Send it with startAfter. |
getTasks, getNotes, getCalendarEvents |
Set to true to include each result’s tasks, notes, or calendar events. |
You can page through results in two ways. Raise page while keeping limit fixed, or send
the startAfter and startAfterId values from the previous response’s meta to continue
from where it ended. Each result includes assignedTo and a nested contact summary with the
contact’s id, name, companyName, email, phone, and tags.
curl "https://services.smbcrm.com/opportunities/search?locationId=<location_id>&pipelineId=<pipeline_id>&status=open&limit=20&page=2" \ -H "Authorization: Bearer <token>" \ -H "Version: v3"{ "opportunities": [ { "id": "<opportunity_id>", "name": "Website redesign - Acme Co.", "pipelineId": "<pipeline_id>", "pipelineStageId": "<pipeline_stage_id>", "status": "open", "contactId": "<contact_id>", "assignedTo": "<user_id>", "monetaryValue": 4200, "contact": { "id": "<contact_id>", "name": "Jordan Lee", "companyName": "Acme Co.", "email": "jordan@example.com", "phone": "+15125550142", "tags": ["website-lead"] }, "createdAt": "2026-07-08T15:04:00.000Z" } ], "meta": { "total": 120, "currentPage": 2, "nextPage": 3, "prevPage": 1, "startAfter": 1625203104328, "startAfterId": "<opportunity_id>" }}Advanced search
Section titled “Advanced search”Send every field in the body.
| Body field | Description |
|---|---|
locationId |
Your account/location ID. |
query |
Full-text search string, up to 75 characters. |
limit |
Results per page. |
page |
Page number, starting at 0. |
searchAfter |
Cursor values for deep pagination, in the form [<timestamp>, "<opportunity_id>"]. |
additionalDetails |
Object with four booleans, all required: notes, tasks, calendarEvents, and unReadConversations. Each one chooses whether to include that detail in the response. |
curl -X POST https://services.smbcrm.com/opportunities/search \ -H "Authorization: Bearer <token>" \ -H "Version: v3" \ -H "Content-Type: application/json" \ -d '{ "locationId": "<location_id>", "query": "Acme", "limit": 20, "page": 0, "searchAfter": [], "additionalDetails": { "notes": false, "tasks": false, "calendarEvents": false, "unReadConversations": false } }'The response lists the matching opportunities and a total. When a pipeline filter applies,
stageAggregations adds one object per stage with totalCount, totalValue, weightedValue,
openValue, openWeightedValue, and wonValue. The weighted values multiply each deal by its
win probability. Use them for pipeline value reports.
{ "opportunities": [ { "id": "<opportunity_id>", "name": "Website redesign - Acme Co.", "pipelineId": "<pipeline_id>", "pipelineStageId": "<pipeline_stage_id>", "status": "open", "contactId": "<contact_id>", "monetaryValue": 4200, "createdAt": "2026-07-08T15:04:00.000Z" } ], "total": 120, "stageAggregations": [ { "pipelineStageId": "<pipeline_stage_id>", "totalCount": 12, "totalValue": 24500, "weightedValue": 14700, "openValue": 18000, "openWeightedValue": 10800, "wonValue": 6500 } ], "aggregations": {}}Update & delete
Section titled “Update & delete”Every body field is optional: name, pipelineId, pipelineStageId, status,
monetaryValue, assignedTo, forecastExpectedCloseDate, forecastProbability, and
customFields. You can’t change the linked contact here. To record a lost reason, use the
status endpoint below. The response is HTTP 200 with the updated opportunity.
curl -X PUT https://services.smbcrm.com/opportunities/<opportunity_id> \ -H "Authorization: Bearer <token>" \ -H "Version: v3" \ -H "Content-Type: application/json" \ -d '{ "name": "Website redesign - Acme Co. (Phase 2)", "pipelineStageId": "<pipeline_stage_id>", "monetaryValue": 6800 }'{ "opportunity": { "id": "<opportunity_id>", "name": "Website redesign - Acme Co. (Phase 2)", "pipelineId": "<pipeline_id>", "pipelineStageId": "<pipeline_stage_id>", "locationId": "<location_id>", "status": "open", "contactId": "<contact_id>", "monetaryValue": 6800, "updatedAt": "2026-07-09T09:30:00.000Z" }}status is required: open, won, lost, or abandoned. When you set it to lost,
pass lostReasonId too so the deal records why it was lost. See
Lost reasons below for valid IDs.
curl -X PUT https://services.smbcrm.com/opportunities/<opportunity_id>/status \ -H "Authorization: Bearer <token>" \ -H "Version: v3" \ -H "Content-Type: application/json" \ -d '{ "status": "won" }'{ "success": true }Check success to confirm the change.
curl -X DELETE https://services.smbcrm.com/opportunities/<opportunity_id> \ -H "Authorization: Bearer <token>" \ -H "Version: v3"{ "success": true }Followers
Section titled “Followers”Followers are the users who get updates on a deal. Both endpoints take a followers array of
user IDs, up to 10 per call. See Users for how to look up user IDs.
curl -X POST https://services.smbcrm.com/opportunities/<opportunity_id>/followers \ -H "Authorization: Bearer <token>" \ -H "Version: v3" \ -H "Content-Type: application/json" \ -d '{ "followers": ["<user_id>"] }'The response lists every follower after the change in followers and the users this call
added in followersAdded.
{ "followers": ["<user_id>", "<user_id>"], "followersAdded": ["<user_id>"]}The followers array in the body is required. To remove all followers from the
opportunity, also set the query parameter isRemoveAllFollowers=true.
curl -X DELETE https://services.smbcrm.com/opportunities/<opportunity_id>/followers \ -H "Authorization: Bearer <token>" \ -H "Version: v3" \ -H "Content-Type: application/json" \ -d '{ "followers": ["<user_id>"] }'The response lists every remaining follower in followers and the users this call removed in
followersRemoved.
{ "followers": ["<user_id>"], "followersRemoved": ["<user_id>"]}Lost reasons
Section titled “Lost reasons”The id of each reason is what you pass as lostReasonId when you set an opportunity’s
status to lost.
| Query parameter | Description |
|---|---|
locationId |
Your account/location ID. Required. |
limit |
Maximum number of reasons to return. Default 100. |
skip |
Number of reasons to skip, for paging. Default 0. |
query |
Search text. |
name |
Filter by lost reason name. |
deleted |
Set to true to list deleted reasons. Default false. |
getCount |
Set to true to return the total count. |
curl "https://services.smbcrm.com/opportunities/lost-reason?locationId=<location_id>" \ -H "Authorization: Bearer <token>" \ -H "Version: v3"{ "lostReasons": [ { "id": "<lost_reason_id>", "name": "Went with a competitor", "locationId": "<location_id>", "createdAt": "2026-01-04T10:00:00.000Z", "updatedAt": "2026-01-04T10:00:00.000Z" } ], "total": 1}Related
Section titled “Related”- Contacts. The
contactIdon an opportunity points to a contact record. - Conversations & Messages. Follow up with the contact tied to a deal.
- Users. The
assignedToandfollowersvalues are user IDs. - Custom Fields, Values & Tags. Define the custom fields you set on a deal.
- Scopes. The permissions your token needs for each endpoint,
including
opportunities.readonly,opportunities.write, and thepipelines.*scopes.
