Appearance
Create an opportunity
Creates an opportunity in a pipeline, optionally placing it in a specific stage and tagging it.
A contact can hold several opportunities in the same pipeline — for example a returning customer placing a second order. How the contact is identified decides whether you get a new opportunity or the existing one:
| You send | Contact | Opportunity |
|---|---|---|
contact_id | That existing contact, used as-is | Always a new one, even if the contact already has opportunities in this pipeline |
contact (phone / email) | Matched on phone or email, or created if no match | The contact's current (newest) opportunity in this pipeline is returned if one exists; a new one is created only when there is none |
Stage selection
If you omit stage_id, the opportunity is placed in the first stage of the pipeline (ordered by order). If you pass stage_id, that stage must belong to the given pipeline.
New lead (match or create the contact)
POST/api/user/opportunities
Required scope:
opportunities:writeRequest body (JSON)
curl -X POST "https://loopx-console.test/api/user/opportunities" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"pipeline_id":17,"stage_id":189,"name":"New lead from website","notes":"Came in via summer campaign","status":"active","context":{"source_campaign":"summer-2026"},"tags":["vip","hot"],"contact":{"name":"Ali Hassan","phone":"201062691152","email":"ali@example.com","priority":"high"}}'Example response
{
"status": true,
"code": 200,
"validation": [],
"message": "Opportunity Created",
"data": {
"id": 98315,
"status": "active",
"contact_id": 44017,
"stage_id": 189,
"pipeline_id": 17,
"name": "New lead from website",
"notes": "Came in via summer campaign",
"context": {
"source_campaign": "summer-2026"
},
"created_at": "2026-07-20T10:00:00.000000Z",
"updated_at": "2026-07-20T10:00:00.000000Z",
"source": null,
"source_id": null,
"contact_name": "Ali Hassan",
"contact_custom_name": null,
"contact_priority": "high",
"contact_identifier": null,
"contact_email": "ali@example.com",
"contact_custom_email": null,
"contact_phone": "201062691152",
"contact_custom_phone": null,
"stage_nature": "neutral",
"stage_name": "New Lead",
"tags": [
{
"id": 12,
"name": "vip"
},
{
"id": 13,
"name": "hot"
}
],
"conversations": []
}
}Further opportunity on an existing contact
Pass contact_id to open another opportunity on a contact you already have — for example to start a new deal after the previous one was won or lost. The contact object is not needed and is ignored when contact_id is sent.
POST/api/user/opportunities
Required scope:
opportunities:writeRequest body (JSON)
curl -X POST "https://loopx-console.test/api/user/opportunities" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"pipeline_id":17,"contact_id":44017,"name":"Second order — 20 units","notes":"Returning customer","tags":["repeat"]}'Example response
{
"status": true,
"code": 200,
"validation": [],
"message": "Opportunity Created",
"data": {
"id": 98410,
"status": "active",
"contact_id": 44017,
"stage_id": 187,
"pipeline_id": 17,
"name": "Second order — 20 units",
"notes": "Returning customer",
"context": null,
"created_at": "2026-09-28T09:00:00.000000Z",
"updated_at": "2026-09-28T09:00:00.000000Z",
"conversation": {
"id": 5521,
"contact_id": 44017,
"status": "active"
}
}
}What happens:
- A new opportunity is created on that contact, in
stage_idor the pipeline's first stage. Earlier opportunities are left untouched, whatever their stage. - The new opportunity joins the contact's existing conversation on the pipeline's channel, so the chat history stays in one thread in the inbox. A conversation is created only if the contact has none there yet.
- It becomes the contact's current opportunity in the pipeline: new inbound messages from the contact are attached to it, and the AI agent sees it first.
- Stage-start automations of its stage run (stage actions, opening message, followups), as for any new opportunity.
- The contact's saved details (custom name, email, phone, priority) are not changed. Use the contact endpoints to edit them.
Which contact ids can I use?
contact_id must be a contact that already has at least one opportunity in any of your pipelines. Find it in the contact_id field of an opportunity (see Retrieve an opportunity).
Request body
| Field | Type | Required | Description |
|---|---|---|---|
pipeline_id | integer | yes | The pipeline to create the opportunity in |
stage_id | integer | no | Place the opportunity in this stage. Defaults to the first stage in the pipeline if omitted |
contact_id | integer | no | An existing contact to open a new opportunity on. When sent, contact.* fields are not required and are ignored. See Further opportunity on an existing contact |
name | string | null | no | Opportunity display name. Max 255 chars |
notes | string | null | no | Free-form notes |
status | enum | no | pending, active, won, lost. Defaults to active |
context | object | null | no | Arbitrary key-value metadata passed to the AI agent alongside the conversation |
tags | string[] | no | Tag names to attach on creation (created if they don't exist yet) |
is_test | boolean | no | Mark as a test opportunity. When true, contact.email and contact.phone must be unique across all contacts |
contact.name | string | null | no | Contact display name |
contact.email | string | null | conditional | Required when neither contact.phone nor contact_id is sent |
contact.phone | string | null | conditional | Required when neither contact.email nor contact_id is sent |
contact.priority | enum | no | low, medium, high. Defaults to medium |
Errors
| Code | When |
|---|---|
403 | Token missing opportunities:write scope |
422 | pipeline_id not found, stage_id doesn't exist, contact_id isn't one of your contacts, none of contact_id / email / phone provided, or (when is_test: true) email / phone already exists |
400 Create a stage first | The pipeline has no stages yet, or stage_id belongs to another pipeline |