Opportunities
Pipelines, their stages, and the opportunities (deals) moving through them.
Pipelines, their stages, and the opportunities (deals) moving through them.
Every route takes Authorization: Bearer <ACCESS_TOKEN> unless the auth line says otherwise. Ids are numbers. Errors use the shape on the errors page.
| Route | Scope | What it does |
|---|---|---|
GET /v1/pipelines | opportunities.read | A workspace's pipelines, each with its stages in order. |
GET /v1/opportunities | opportunities.read | Opportunities in a workspace, filtered and paginated. |
GET /v1/opportunities/{id} | opportunities.read | One opportunity with its pipeline, stage and contact. |
POST /v1/opportunities | opportunities.write | Create an opportunity for a contact. |
PATCH /v1/opportunities/{id} | opportunities.write | Change name, value, currency, source, owner, tags or custom fields. |
PATCH /v1/opportunities/{id}/stage | opportunities.write | Move an opportunity to another stage of its pipeline. |
PATCH /v1/opportunities/{id}/status | opportunities.write | Win, lose, abandon or reopen an opportunity. |
DELETE /v1/opportunities/{id} | opportunities.write | Delete an opportunity. |
GET /v1/opportunities/{id}/activities | opportunities.read | An opportunity's history, newest first. |
List pipelines
GET https://api.bluereacher.com/v1/pipelinesAuth: Bearer access token. Scope: opportunities.read.
A workspace's pipelines, each with its stages in order.
A workspace that has never opened its Pipeline gets the default pipeline created on the first call, as in the Blue Reacher dashboard.
Use a stage id from here for Create opportunity and Move stage. is_won / is_lost mark the closing stages.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id | query | number | yes | The workspace. |
Request
curl -X GET "https://api.bluereacher.com/v1/pipelines?workspace_id=7" \
-H "Authorization: Bearer <ACCESS_TOKEN>"Responses
A 200 reply (OK):
{
"success": true,
"data": [
{
"id": 3,
"name": "Sales",
"is_default": true,
"position": 0,
"stages": [
{
"id": 8,
"name": "New lead",
"color": "#94a3b8",
"position": 0,
"is_won": false,
"is_lost": false
},
{
"id": 9,
"name": "Qualified",
"color": "#22c55e",
"position": 1,
"is_won": false,
"is_lost": false
},
{
"id": 10,
"name": "Won",
"color": "#16a34a",
"position": 2,
"is_won": true,
"is_lost": false
}
],
"created_at": "2026-09-01T10:00:00.000Z",
"updated_at": "2026-10-01T10:00:00.000Z"
}
]
}| Status | When | Message |
|---|---|---|
| 200 | OK | |
| 400 | workspace_id missing | workspace_id is required |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope(s): opportunities.read. This integration was granted: … |
| 404 | workspace_id does not exist or is not owned by the installer (same response for both) | Workspace not found or not accessible to this token |
List opportunities
GET https://api.bluereacher.com/v1/opportunitiesAuth: Bearer access token. Scope: opportunities.read.
Opportunities in a workspace, filtered and paginated.
Filters combine with AND. search matches the opportunity name or the linked contact's name or email.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id | query | number | yes | The workspace. |
pipeline_id | query | number | no | Only this pipeline. |
stage_id | query | number | no | Only this stage. |
status | query | enum | no | Only this status. |
contact_id | query | number | no | Only this contact's opportunities. |
owner_id | query | number | no | Only this owner. |
source | query | string | no | Source contains this text. |
search | query | string | no | Name, or contact name / email. |
sort | query | enum | no | Default created_at. |
order | query | enum | no | Default desc. |
page | query | number | no | Default 1. |
limit | query | number | no | 1–100, default 20. Anything else is a 400. |
Request
curl -X GET "https://api.bluereacher.com/v1/opportunities?workspace_id=7&page=1&limit=20" \
-H "Authorization: Bearer <ACCESS_TOKEN>"Responses
A 200 reply (OK):
{
"success": true,
"data": [
{
"id": 55,
"workspace_id": 7,
"name": "Website redesign",
"value": 1200,
"currency": "USD",
"status": "open",
"lost_reason": null,
"pipeline_id": 3,
"pipeline": {
"id": 3,
"name": "Sales"
},
"stage_id": 9,
"stage": {
"id": 9,
"name": "Qualified",
"color": "#22c55e",
"position": 1,
"is_won": false,
"is_lost": false
},
"contact_id": 1234,
"contact": {
"id": 1234,
"first_name": "Ann",
"last_name": "Lee",
"email": "ann@example.com",
"phone_number": "+61412345678",
"company_name": "Lee Studio"
},
"owner_id": null,
"source": "website",
"tags": [
"web"
],
"custom_fields": {},
"closed_at": null,
"stage_changed_at": "2026-10-27T09:00:00.000Z",
"last_activity_at": "2026-10-27T09:00:00.000Z",
"created_at": "2026-10-27T09:00:00.000Z",
"updated_at": "2026-10-27T09:00:00.000Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"max_limit": 100,
"total": 1,
"total_pages": 1,
"sort": "created_at",
"order": "desc"
}
}| Status | When | Message |
|---|---|---|
| 200 | OK | |
| 400 | workspace_id missing | workspace_id is required |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope(s): opportunities.read. This integration was granted: … |
| 404 | workspace_id does not exist or is not owned by the installer (same response for both) | Workspace not found or not accessible to this token |
Response fields
opportunity: Each item in data. Fields:
| Field | Type | Description |
|---|---|---|
id | number | Opportunity id. Example: 55 |
workspace_id | number | The workspace it belongs to. Example: 7 |
name | string | Opportunity name. Example: Website redesign |
value | number | Deal value. Example: 1200 |
currency | string | ISO currency code. Example: USD |
status | string | "open", "won", "lost" or "abandoned". Example: open |
lost_reason | string, nullable | Set while status is "lost". |
pipeline | object | id and name of its pipeline. |
stage | object | id, name, color, position, is_won, is_lost of its stage. |
contact | object | The linked contact: id, first_name, last_name, email, phone_number, company_name. |
owner_id | number, nullable | The assigned user, when set. |
source | string, nullable | Free text: where the deal came from. |
tags | array<string> | Opportunity tags. |
custom_fields | object | Opportunity custom field values by key. |
closed_at | string (ISO 8601), nullable | When it was won / lost / abandoned; null while open. |
stage_changed_at | string (ISO 8601) | When it entered its current stage. |
last_activity_at | string (ISO 8601) | Last change of any kind. |
Get opportunity
GET https://api.bluereacher.com/v1/opportunities/{id}Auth: Bearer access token. Scope: opportunities.read.
One opportunity with its pipeline, stage and contact.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | number | yes | Opportunity id. |
Request
curl -X GET "https://api.bluereacher.com/v1/opportunities/55" \
-H "Authorization: Bearer <ACCESS_TOKEN>"Responses
A 200 reply (OK):
{
"success": true,
"data": {
"id": 55,
"workspace_id": 7,
"name": "Website redesign",
"value": 1200,
"currency": "USD",
"status": "open",
"lost_reason": null,
"pipeline_id": 3,
"pipeline": {
"id": 3,
"name": "Sales"
},
"stage_id": 9,
"stage": {
"id": 9,
"name": "Qualified",
"color": "#22c55e",
"position": 1,
"is_won": false,
"is_lost": false
},
"contact_id": 1234,
"contact": {
"id": 1234,
"first_name": "Ann",
"last_name": "Lee",
"email": "ann@example.com",
"phone_number": "+61412345678",
"company_name": "Lee Studio"
},
"owner_id": null,
"source": "website",
"tags": [
"web"
],
"custom_fields": {},
"closed_at": null,
"stage_changed_at": "2026-10-27T09:00:00.000Z",
"last_activity_at": "2026-10-27T09:00:00.000Z",
"created_at": "2026-10-27T09:00:00.000Z",
"updated_at": "2026-10-27T09:00:00.000Z"
}
}| Status | When | Message |
|---|---|---|
| 200 | OK | |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope(s): opportunities.read. This integration was granted: … |
| 404 | Opportunity does not exist or is not owned by the installer | Opportunity not found or not accessible to this token |
Response fields
opportunity. Fields:
| Field | Type | Description |
|---|---|---|
id | number | Opportunity id. Example: 55 |
workspace_id | number | The workspace it belongs to. Example: 7 |
name | string | Opportunity name. Example: Website redesign |
value | number | Deal value. Example: 1200 |
currency | string | ISO currency code. Example: USD |
status | string | "open", "won", "lost" or "abandoned". Example: open |
lost_reason | string, nullable | Set while status is "lost". |
pipeline | object | id and name of its pipeline. |
stage | object | id, name, color, position, is_won, is_lost of its stage. |
contact | object | The linked contact: id, first_name, last_name, email, phone_number, company_name. |
owner_id | number, nullable | The assigned user, when set. |
source | string, nullable | Free text: where the deal came from. |
tags | array<string> | Opportunity tags. |
custom_fields | object | Opportunity custom field values by key. |
closed_at | string (ISO 8601), nullable | When it was won / lost / abandoned; null while open. |
stage_changed_at | string (ISO 8601) | When it entered its current stage. |
last_activity_at | string (ISO 8601) | Last change of any kind. |
Create opportunity
POST https://api.bluereacher.com/v1/opportunitiesAuth: Bearer access token. Scope: opportunities.write.
Create an opportunity for a contact.
The contact must be in the workspace. Without pipeline_id the default pipeline is used; without stage_id the stage named in stage_name is used, else the pipeline's first stage.
Creating one runs any workflow that triggers on Opportunity created, as in the dashboard.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id | body | number | yes | The workspace. |
contact_id | body | number | yes | A contact in that workspace. |
name | body | string | yes | Up to 191 characters. |
pipeline_id | body | number | no | Default: the default pipeline. |
stage_id | body | number | no | A stage of that pipeline. |
stage_name | body | string | no | Or the stage by name (case-insensitive). |
value | body | number | no | Default 0. |
currency | body | string | no | Default USD. |
status | body | enum | no | Default open. |
source | body | string | no | Where it came from. |
owner_id | body | number | no | The assigned user. |
tags | body | array<string> | no | Opportunity tags. |
custom_fields | body | object | no | Opportunity custom field values by key. |
Request
curl -X POST "https://api.bluereacher.com/v1/opportunities" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"workspace_id": 7,
"contact_id": 1234,
"name": "Website redesign",
"value": 1200,
"stage_name": "Qualified",
"source": "website"
}'Responses
A 201 reply (Created):
{
"success": true,
"message": "Opportunity created",
"data": {
"id": 55,
"workspace_id": 7,
"name": "Website redesign",
"value": 1200,
"currency": "USD",
"status": "open",
"lost_reason": null,
"pipeline_id": 3,
"pipeline": {
"id": 3,
"name": "Sales"
},
"stage_id": 9,
"stage": {
"id": 9,
"name": "Qualified",
"color": "#22c55e",
"position": 1,
"is_won": false,
"is_lost": false
},
"contact_id": 1234,
"contact": {
"id": 1234,
"first_name": "Ann",
"last_name": "Lee",
"email": "ann@example.com",
"phone_number": "+61412345678",
"company_name": "Lee Studio"
},
"owner_id": null,
"source": "website",
"tags": [
"web"
],
"custom_fields": {},
"closed_at": null,
"stage_changed_at": "2026-10-27T09:00:00.000Z",
"last_activity_at": "2026-10-27T09:00:00.000Z",
"created_at": "2026-10-27T09:00:00.000Z",
"updated_at": "2026-10-27T09:00:00.000Z"
}
}| Status | When | Message |
|---|---|---|
| 201 | Created | |
| 400 | Missing field, or a contact / pipeline / stage that does not fit | Contact not found in this workspace |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope(s): opportunities.write. This integration was granted: … |
| 404 | workspace_id does not exist or is not owned by the installer (same response for both) | Workspace not found or not accessible to this token |
Response fields
opportunity. Fields:
| Field | Type | Description |
|---|---|---|
id | number | Opportunity id. Example: 55 |
workspace_id | number | The workspace it belongs to. Example: 7 |
name | string | Opportunity name. Example: Website redesign |
value | number | Deal value. Example: 1200 |
currency | string | ISO currency code. Example: USD |
status | string | "open", "won", "lost" or "abandoned". Example: open |
lost_reason | string, nullable | Set while status is "lost". |
pipeline | object | id and name of its pipeline. |
stage | object | id, name, color, position, is_won, is_lost of its stage. |
contact | object | The linked contact: id, first_name, last_name, email, phone_number, company_name. |
owner_id | number, nullable | The assigned user, when set. |
source | string, nullable | Free text: where the deal came from. |
tags | array<string> | Opportunity tags. |
custom_fields | object | Opportunity custom field values by key. |
closed_at | string (ISO 8601), nullable | When it was won / lost / abandoned; null while open. |
stage_changed_at | string (ISO 8601) | When it entered its current stage. |
last_activity_at | string (ISO 8601) | Last change of any kind. |
Update opportunity
PATCH https://api.bluereacher.com/v1/opportunities/{id}Auth: Bearer access token. Scope: opportunities.write.
Change name, value, currency, source, owner, tags or custom fields.
To move it to another stage use Move stage; to win, lose or reopen it use Change status. Those run their own workflow triggers.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | number | yes | Opportunity id. |
name | body | string | no | Up to 191 characters. |
value | body | number | no | Deal value. |
currency | body | string | no | ISO currency code. |
source | body | string | no | Where it came from. |
owner_id | body | number | no | The assigned user. |
tags | body | array<string> | no | Replaces the tags. |
custom_fields | body | object | no | Custom field values by key. |
Request
curl -X PATCH "https://api.bluereacher.com/v1/opportunities/55" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"value": 1500
}'Responses
A 200 reply (OK):
{
"success": true,
"message": "Opportunity updated",
"data": {
"id": 55,
"workspace_id": 7,
"name": "Website redesign",
"value": 1500,
"currency": "USD",
"status": "open",
"lost_reason": null,
"pipeline_id": 3,
"pipeline": {
"id": 3,
"name": "Sales"
},
"stage_id": 9,
"stage": {
"id": 9,
"name": "Qualified",
"color": "#22c55e",
"position": 1,
"is_won": false,
"is_lost": false
},
"contact_id": 1234,
"contact": {
"id": 1234,
"first_name": "Ann",
"last_name": "Lee",
"email": "ann@example.com",
"phone_number": "+61412345678",
"company_name": "Lee Studio"
},
"owner_id": null,
"source": "website",
"tags": [
"web"
],
"custom_fields": {},
"closed_at": null,
"stage_changed_at": "2026-10-27T09:00:00.000Z",
"last_activity_at": "2026-10-27T09:00:00.000Z",
"created_at": "2026-10-27T09:00:00.000Z",
"updated_at": "2026-10-27T09:00:00.000Z"
}
}| Status | When | Message |
|---|---|---|
| 200 | OK | |
| 400 | Nothing to update | Nothing to update — send name, value, currency, source, owner_id, tags or custom_fields. |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope(s): opportunities.write. This integration was granted: … |
| 404 | Opportunity does not exist or is not owned by the installer | Opportunity not found or not accessible to this token |
Move stage
PATCH https://api.bluereacher.com/v1/opportunities/{id}/stageAuth: Bearer access token. Scope: opportunities.write.
Move an opportunity to another stage of its pipeline.
Runs any workflow that triggers on Opportunity stage changed. Moving it to the stage it is already in changes nothing.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | number | yes | Opportunity id. |
stage_id | body | number | yes | A stage of the same pipeline (from List pipelines). |
Request
curl -X PATCH "https://api.bluereacher.com/v1/opportunities/55/stage" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"stage_id": 10
}'Responses
A 200 reply (OK):
{
"success": true,
"message": "Stage changed",
"data": {
"id": 55,
"workspace_id": 7,
"name": "Website redesign",
"value": 1200,
"currency": "USD",
"status": "open",
"lost_reason": null,
"pipeline_id": 3,
"pipeline": {
"id": 3,
"name": "Sales"
},
"stage_id": 10,
"stage": {
"id": 10,
"name": "Won",
"color": "#16a34a",
"position": 2,
"is_won": true,
"is_lost": false
},
"contact_id": 1234,
"contact": {
"id": 1234,
"first_name": "Ann",
"last_name": "Lee",
"email": "ann@example.com",
"phone_number": "+61412345678",
"company_name": "Lee Studio"
},
"owner_id": null,
"source": "website",
"tags": [
"web"
],
"custom_fields": {},
"closed_at": null,
"stage_changed_at": "2026-10-27T09:00:00.000Z",
"last_activity_at": "2026-10-27T09:00:00.000Z",
"created_at": "2026-10-27T09:00:00.000Z",
"updated_at": "2026-10-27T09:00:00.000Z"
}
}| Status | When | Message |
|---|---|---|
| 200 | OK | |
| 400 | Missing or foreign stage | stage_id is required |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope(s): opportunities.write. This integration was granted: … |
| 404 | Opportunity does not exist or is not owned by the installer | Opportunity not found or not accessible to this token |
Change status
PATCH https://api.bluereacher.com/v1/opportunities/{id}/statusAuth: Bearer access token. Scope: opportunities.write.
Win, lose, abandon or reopen an opportunity.
Runs any workflow that triggers on Opportunity status changed. lost_reason is kept only while the status is "lost"; reopening clears it.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | number | yes | Opportunity id. |
status | body | enum | yes | The new status. |
lost_reason | body | string | no | Up to 255 characters, for "lost". |
Request
curl -X PATCH "https://api.bluereacher.com/v1/opportunities/55/status" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"status": "lost",
"lost_reason": "Went with another agency"
}'Responses
A 200 reply (OK):
{
"success": true,
"message": "Status changed",
"data": {
"id": 55,
"workspace_id": 7,
"name": "Website redesign",
"value": 1200,
"currency": "USD",
"status": "lost",
"lost_reason": "Went with another agency",
"pipeline_id": 3,
"pipeline": {
"id": 3,
"name": "Sales"
},
"stage_id": 9,
"stage": {
"id": 9,
"name": "Qualified",
"color": "#22c55e",
"position": 1,
"is_won": false,
"is_lost": false
},
"contact_id": 1234,
"contact": {
"id": 1234,
"first_name": "Ann",
"last_name": "Lee",
"email": "ann@example.com",
"phone_number": "+61412345678",
"company_name": "Lee Studio"
},
"owner_id": null,
"source": "website",
"tags": [
"web"
],
"custom_fields": {},
"closed_at": "2026-10-28T11:00:00.000Z",
"stage_changed_at": "2026-10-27T09:00:00.000Z",
"last_activity_at": "2026-10-27T09:00:00.000Z",
"created_at": "2026-10-27T09:00:00.000Z",
"updated_at": "2026-10-27T09:00:00.000Z"
}
}| Status | When | Message |
|---|---|---|
| 200 | OK | |
| 400 | Unknown status | status must be one of: open, won, lost, abandoned |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope(s): opportunities.write. This integration was granted: … |
| 404 | Opportunity does not exist or is not owned by the installer | Opportunity not found or not accessible to this token |
Delete opportunity
DELETE https://api.bluereacher.com/v1/opportunities/{id}Auth: Bearer access token. Scope: opportunities.write.
Delete an opportunity.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | number | yes | Opportunity id. |
Request
curl -X DELETE "https://api.bluereacher.com/v1/opportunities/55" \
-H "Authorization: Bearer <ACCESS_TOKEN>"Responses
A 200 reply (OK):
{
"success": true,
"message": "Opportunity deleted"
}| Status | When | Message |
|---|---|---|
| 200 | OK | |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope(s): opportunities.write. This integration was granted: … |
| 404 | Opportunity does not exist or is not owned by the installer | Opportunity not found or not accessible to this token |
List activities
GET https://api.bluereacher.com/v1/opportunities/{id}/activitiesAuth: Bearer access token. Scope: opportunities.read.
An opportunity's history, newest first.
One row per change: created, stage changed, status changed, value changed and so on. details carries what changed.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | number | yes | Opportunity id. |
page | query | number | no | Default 1. |
limit | query | number | no | 1–100, default 20. Anything else is a 400. |
Request
curl -X GET "https://api.bluereacher.com/v1/opportunities/55/activities" \
-H "Authorization: Bearer <ACCESS_TOKEN>"Responses
A 200 reply (OK):
{
"success": true,
"data": [
{
"id": 2,
"type": "stage_changed",
"from_stage": {
"id": 8,
"name": "New lead"
},
"to_stage": {
"id": 9,
"name": "Qualified"
},
"from_status": null,
"to_status": null,
"details": null,
"created_at": "2026-10-27T10:00:00.000Z"
},
{
"id": 1,
"type": "created",
"from_stage": null,
"to_stage": {
"id": 8,
"name": "New lead"
},
"from_status": null,
"to_status": "open",
"details": {
"to_value": 1200
},
"created_at": "2026-10-27T09:00:00.000Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"max_limit": 100,
"total": 2,
"total_pages": 1
}
}| Status | When | Message |
|---|---|---|
| 200 | OK | |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope(s): opportunities.read. This integration was granted: … |
| 404 | Opportunity does not exist or is not owned by the installer | Opportunity not found or not accessible to this token |

