Appearance
List a contact's opportunities
Returns every opportunity linked to a contact, across all of your pipelines, newest first. Each item has the same shape as Retrieve an opportunity.
Lookup by contact id or phone
The {identifier} in the URL accepts either:
- The numeric contact id (e.g.
44016) - A contact phone number in the same format your channels store it (digits only, country code prefix, no
+). Example:201062691152.
A numeric identifier is tried as a contact id first. If no opportunities match that id, the server retries it as a phone number.
Narrow the list with pipeline_id
A contact can hold opportunities in several pipelines. Pass an optional pipeline_id query parameter to only return the opportunities from one pipeline.
Empty result
If nothing matches, the endpoint returns 200 with an empty data array. It does not return 404.
GET/api/user/contacts/{identifier}/opportunities
Required scope:
opportunities:readPath parameters
integer | string
Query parameters
integer
curl -X GET "https://loopx-console.test/api/user/contacts/44016/opportunities" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json"Example response
{
"status": true,
"code": 200,
"validation": [],
"message": "Success",
"data": [
{
"id": 98314,
"status": "active",
"contact_id": 44016,
"stage_id": 189,
"pipeline_id": 17,
"name": null,
"notes": null,
"created_at": "2026-07-06T12:28:57.000000Z",
"updated_at": "2026-07-06T12:28:57.000000Z",
"source": "facebook",
"source_id": "120248819452900116",
"contact_name": "Ali",
"contact_custom_name": null,
"contact_priority": null,
"contact_identifier": "36537370685479@lid",
"contact_email": null,
"contact_custom_email": null,
"contact_phone": "201062691152",
"contact_custom_phone": null,
"stage_nature": "neutral",
"stage_name": "New Lead",
"tags": [],
"conversations": [
{
"id": 89328,
"is_test": false,
"last_message_at": "2026-07-06T13:10:21.000000Z",
"messages_count": 2,
"cost_for_user": 0.014633325,
"status": "active",
"last_message_content": "…last AI reply preview…",
"avg_response_time": 0,
"pivot": {
"opportunity_id": 98314,
"conversation_id": 89328
}
}
]
}
]
}Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
identifier | integer | string | yes | Contact id or contact phone number |
Query parameters
| Name | Type | Description |
|---|---|---|
pipeline_id | integer | Optional. Only return opportunities from this pipeline. |
Response fields
data is an array of opportunities, ordered by created_at descending. The list is not paginated. Each item has the same fields as Retrieve an opportunity:
| Field | Type | Description |
|---|---|---|
id | integer | Opportunity id |
status | string | active, paused, won, lost |
contact_id | integer | Related contact id |
stage_id, stage_name, stage_nature | integer, string, string | Current stage |
pipeline_id | integer | Pipeline this opportunity belongs to |
name | string | null | Opportunity display name |
notes | string | null | Free-form notes |
source, source_id | string | Channel and channel-specific id (e.g. facebook, Meta thread id) |
contact_* | mixed | Flattened contact fields (name, phone, email, priority, identifier). _custom_* variants are user overrides on top of the raw contact record. |
tags | array | Tags attached to the opportunity |
conversations | array | Conversations linked to this opportunity — one row per conversation with messages_count, last_message_at, cost_for_user, etc. |
Errors
| Code | When |
|---|---|
403 | Token missing opportunities:read scope |