Skip to content

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:read
Path 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"
<?php

use GuzzleHttp\Client;

$client = new Client();
$response = $client->request('GET', 'https://loopx-console.test/api/user/contacts/44016/opportunities', [
    'headers' => [
        'Authorization' => 'Bearer $TOKEN',
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
const res = await fetch('https://loopx-console.test/api/user/contacts/44016/opportunities', {
  method: 'GET',
  headers: {
    Authorization: 'Bearer $TOKEN',
    Accept: 'application/json'
  }
});

const data = await res.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 ​

NameTypeRequiredDescription
identifierinteger | stringyesContact id or contact phone number

Query parameters ​

NameTypeDescription
pipeline_idintegerOptional. 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:

FieldTypeDescription
idintegerOpportunity id
statusstringactive, paused, won, lost
contact_idintegerRelated contact id
stage_id, stage_name, stage_natureinteger, string, stringCurrent stage
pipeline_idintegerPipeline this opportunity belongs to
namestring | nullOpportunity display name
notesstring | nullFree-form notes
source, source_idstringChannel and channel-specific id (e.g. facebook, Meta thread id)
contact_*mixedFlattened contact fields (name, phone, email, priority, identifier). _custom_* variants are user overrides on top of the raw contact record.
tagsarrayTags attached to the opportunity
conversationsarrayConversations linked to this opportunity — one row per conversation with messages_count, last_message_at, cost_for_user, etc.

Errors ​

CodeWhen
403Token missing opportunities:read scope