> ## Documentation Index
> Fetch the complete documentation index at: https://docs.surge.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Ongoing Conversations

> How contacts and conversation threads work, when to use them, and how to manage opt-out state per thread.

When the same person sends and receives multiple messages over time, Surge tracks them as a conversation thread. Understanding how conversations and contacts work helps you build two-way messaging flows, surface message history, and manage opt-out state correctly.

## Contacts and conversations

A **contact** represents a phone number in the context of a specific account. When someone messages your number for the first time, or when you first message them, Surge creates a contact record and a conversation thread.

A **conversation** is the thread of all messages between your number and a contact. Every message you send or receive includes a `conversation.id` in the response.

```json theme={null}
{
  "id": "msg_01jrzhe8d9enptypyx360pcmxj",
  "blast_id": null,
  "body": "Your order is ready for pickup.",
  "attachments": [],
  "conversation": {
    "id": "cnv_01jav8xy7fe4nsay3c9deqxge9",
    "phone_number": {
      "id": "pn_01jsjwe4d9fx3tpymgtg958d9w",
      "number": "+18015556789",
      "type": "local"
    },
    "contact": {
      "id": "ctc_01ja88cboqffhswjx8zbak3ykk",
      "phone_number": "+18015551234"
    }
  },
  "metadata": {}
}
```

## When to use contacts vs raw messaging

**Raw messaging** (passing a phone number directly in the `to` field) is fine for simple one-way notifications where you don't need to track who sent what or manage opt-out state per person. Blast sends work this way.

**Contacts** become useful when:

* You're building two-way messaging and need to correlate inbound replies with specific customers
* You need to check or manage opt-out state for an individual
* You want to associate metadata (name, account ID in your system) with a phone number
* You're building embeddable UI components that need to show a contact's message history

Create a contact explicitly to attach metadata before the first message:

<CodeGroup>
  ```python Python theme={null}
  from surge import Surge

  surge = Surge()

  contact = surge.contacts.create(
      account_id="{account_id}",
      phone_number="+18015551234",
      first_name="Jordan",
      last_name="Patel",
      email="jordan.patel@example.com",
      metadata={"customer_id": "cust_9887"},
  )
  ```

  ```typescript TypeScript theme={null}
  import Surge from "@surgeapi/node";

  const surge = new Surge();

  const contact = await surge.contacts.create("{account_id}", {
    phone_number: "+18015551234",
    first_name: "Jordan",
    last_name: "Patel",
    email: "jordan.patel@example.com",
    metadata: { customer_id: "cust_9887" },
  });
  ```

  ```ruby Ruby theme={null}
  require "surge_api"

  surge = SurgeAPI::Client.new

  contact = surge.contacts.create(
    "{account_id}",
    phone_number: "+18015551234",
    first_name: "Jordan",
    last_name: "Patel",
    email: "jordan.patel@example.com",
    metadata: { "customer_id" => "cust_9887" }
  )
  ```

  ```elixir Elixir theme={null}
  client = Surge.Client.new(System.get_env("SURGE_API_KEY"))

  {:ok, contact} =
    Surge.Contacts.create(client, "{account_id}", %{
      phone_number: "+18015551234",
      first_name: "Jordan",
      last_name: "Patel",
      email: "jordan.patel@example.com",
      metadata: %{customer_id: "cust_9887"}
    })
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.surge.app/accounts/{account_id}/contacts \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "phone_number": "+18015551234",
      "first_name": "Jordan",
      "last_name": "Patel",
      "email": "jordan.patel@example.com",
      "metadata": {
        "customer_id": "cust_9887"
      }
    }'
  ```
</CodeGroup>

```json theme={null}
{
  "id": "ctc_01jrzhe8d9enptypyx360pcmxn",
  "phone_number": "+18015551234",
  "first_name": "Jordan",
  "last_name": "Patel",
  "email": "jordan.patel@example.com",
  "metadata": {
    "customer_id": "cust_9887"
  }
}
```

Contacts support these fields: `phone_number` (required), `first_name`, `last_name`, `email`, `date_of_birth`, and `metadata`.

<Note>
  **All contact PII is encrypted at rest.** `first_name`, `last_name`, `email`, `date_of_birth`, `phone_number`, and `metadata` are stored encrypted in the database. You can safely store customer-identifying information here without it being readable if the database is compromised.
</Note>

Surge also automatically populates carrier data (`phone_carrier`, `phone_carrier_name`, `phone_country`) on contacts based on carrier lookups. This data is populated asynchronously after the first message send or verification — it is not available at contact creation time.

## Following a conversation

When a contact sends you an inbound message, the [`message.received`](../receiving/events#message-received) webhook event includes the conversation ID. Use that ID to fetch the full thread or send a reply into the same conversation.

<CodeGroup>
  ```python Python theme={null}
  # Send a reply into an existing conversation
  message = surge.messages.create(
      account_id="{account_id}",
      conversation={"id": "cnv_01jav8xy7fe4nsay3c9deqxge9"},
      body="Your order is at the front desk, no signature needed.",
  )
  ```

  ```typescript TypeScript theme={null}
  const message = await surge.messages.create("{account_id}", {
    conversation: { id: "cnv_01jav8xy7fe4nsay3c9deqxge9" },
    body: "Your order is at the front desk, no signature needed.",
  });
  ```

  ```ruby Ruby theme={null}
  message = surge.messages.create(
    "{account_id}",
    conversation: { id: "cnv_01jav8xy7fe4nsay3c9deqxge9" },
    body: "Your order is at the front desk, no signature needed."
  )
  ```

  ```elixir Elixir theme={null}
  {:ok, message} =
    Surge.Messages.create(client, "{account_id}", %{
      conversation: %{id: "cnv_01jav8xy7fe4nsay3c9deqxge9"},
      body: "Your order is at the front desk, no signature needed."
    })
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.surge.app/accounts/{account_id}/messages \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "conversation": {"id": "cnv_01jav8xy7fe4nsay3c9deqxge9"},
      "body": "Your order is at the front desk, no signature needed."
    }'
  ```
</CodeGroup>

When you send into a conversation, Surge routes the message from the same number the contact previously reached.

## Conversation status

A conversation has one of three statuses:

| Status      | Meaning                                                                                                                                            |
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `active`    | The conversation is open; messages can be sent and received                                                                                        |
| `archived`  | The conversation has been closed in the dashboard but the contact has not opted out. Sending to an archived conversation re-opens it automatically |
| `opted_out` | The contact replied with a recognized opt-out keyword. Sending returns an `opted_out` error until they re-opt-in                                   |

The `opted_out_at` timestamp is set when the conversation moves to `opted_out` status.

## Opt-out state

Opt-out state lives on the conversation (one contact might opt out of one number but remain active on another). When a contact replies STOP, the `conversation.opted_out_at` timestamp is set and the `contact.opted_out` webhook event fires.

Sending to an opted-out conversation returns an `opted_out` error.

<Warning>
  Do not attempt to send around opt-outs. Doing so violates carrier policies and can result in your campaign being deactivated.
</Warning>

A contact re-opts in by texting START, YES, or UNSTOP to the same number. When that happens, `contact.opted_in` fires and the conversation status returns to `active`.

## Audiences

If you want to group contacts into named lists for repeated sends, use audiences. An audience is a collection of contacts that you blast together. See [Send to Many People](./send-many) for the full pattern.

## Cross-channel tracking (roadmap)

<Info>
  Conversations and contacts today are SMS-specific. As Surge adds RCS, WhatsApp, and other channels, the contact model will extend to track communication history across all channels in a single thread. See [Channels Roadmap](../channels-roadmap/rcs-and-whatsapp).
</Info>
