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

# Integration examples

> End-to-end scenarios with the Nuvia API: messages, webhooks, campaigns, and contacts

Complete use cases you can run for real with an API key. All calls use the
`Authorization: Bearer` header. See [Authentication](/en/autenticacao) and the [API Key guide](/en/guia-api-key)
to get and configure the key. Base: `https://api.nuvia.ai`, prefix `/v1`.

Each case lists the required API key **scopes**. Grant only those when creating the key.

## Case 1: Send your first message

**Scopes:** `inboxes:read`, `conversations:read`, `messages:read`, `messages:create`.

<Steps>
  <Step title="Discover your channels (inboxes)">
    List the inboxes connected to your company.

    <CodeGroup>
      ```bash cURL theme={null}
      curl https://api.nuvia.ai/v1/inboxes \
        -H "Authorization: Bearer $NUVIA_API_KEY"
      ```

      ```javascript JavaScript theme={null}
      const res = await fetch("https://api.nuvia.ai/v1/inboxes", {
        headers: { Authorization: `Bearer ${process.env.NUVIA_API_KEY}` },
      });
      const { data } = await res.json();
      ```

      ```python Python theme={null}
      import os, requests
      res = requests.get(
          "https://api.nuvia.ai/v1/inboxes",
          headers={"Authorization": f"Bearer {os.environ['NUVIA_API_KEY']}"},
      )
      data = res.json()["data"]
      ```
    </CodeGroup>
  </Step>

  <Step title="Get a conversation">
    Sending a message happens inside a conversation. List conversations to get a
    `conversationId` (or create one with `POST /v1/conversations`).

    ```bash cURL theme={null}
    curl "https://api.nuvia.ai/v1/conversations" \
      -H "Authorization: Bearer $NUVIA_API_KEY"
    ```
  </Step>

  <Step title="Send a text message">
    `POST /v1/messages/send` with the `conversationId` and the `message` object.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X POST https://api.nuvia.ai/v1/messages/send \
        -H "Authorization: Bearer $NUVIA_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "conversationId": "CONVERSATION_ID",
          "message": { "content": "Hello! Sent via API.", "contentType": "TEXT" }
        }'
      ```

      ```javascript JavaScript theme={null}
      await fetch("https://api.nuvia.ai/v1/messages/send", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.NUVIA_API_KEY}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          conversationId: "CONVERSATION_ID",
          message: { content: "Hello! Sent via API.", contentType: "TEXT" },
        }),
      });
      ```

      ```python Python theme={null}
      import os, requests
      requests.post(
          "https://api.nuvia.ai/v1/messages/send",
          headers={"Authorization": f"Bearer {os.environ['NUVIA_API_KEY']}"},
          json={
              "conversationId": "CONVERSATION_ID",
              "message": {"content": "Hello! Sent via API.", "contentType": "TEXT"},
          },
      )
      ```
    </CodeGroup>

    Use `contentType: "TEXT"` for text messages.
  </Step>

  <Step title="Read the conversation history">
    `GET /v1/messages/conversation/:id` returns the messages; attachments are at
    `GET /v1/messages/conversation/:id/attachments`.

    ```bash cURL theme={null}
    curl "https://api.nuvia.ai/v1/messages/conversation/CONVERSATION_ID" \
      -H "Authorization: Bearer $NUVIA_API_KEY"
    ```
  </Step>
</Steps>

## Case 2: Receive events via webhooks

Register a URL of yours for Nuvia to notify in real time (messages, conversations, contacts). This
case shows the end-to-end flow; the payload format, the full list of events, and the delivery
guarantees are in the [Webhooks guide](/en/guias/webhooks).

**Scopes:** to register and query, `webhooks:create` and `webhooks:read`; to manage,
`webhooks:update` and `webhooks:delete` (+ `messages:create` if you reply automatically).

<Steps>
  <Step title="Register a webhook">
    `POST /v1/webhooks`. There are 10 valid events: `MESSAGE_SENT`, `MESSAGE_RECEIVED`,
    `MESSAGE_DELIVERED`, `MESSAGE_READ`, `CONVERSATION_CREATED`, `CONVERSATION_UPDATED`,
    `CONTACT_CREATED`, `CONTACT_UPDATED`, `FOLLOWUP_SENT`, and `FIELD_UPDATED`. See what each one
    delivers in the [event table](/en/guias/webhooks).

    Your company has **one** webhook: if one is already configured, this call returns `409`.
    Update the existing one instead of creating another.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X POST https://api.nuvia.ai/v1/webhooks \
        -H "Authorization: Bearer $NUVIA_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "url": "https://your-server.com/nuvia/webhook",
          "events": ["MESSAGE_RECEIVED", "CONVERSATION_CREATED"],
          "status": "ACTIVE"
        }'
      ```

      ```javascript JavaScript theme={null}
      await fetch("https://api.nuvia.ai/v1/webhooks", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.NUVIA_API_KEY}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          url: "https://your-server.com/nuvia/webhook",
          events: ["MESSAGE_RECEIVED", "CONVERSATION_CREATED"],
          status: "ACTIVE",
        }),
      });
      ```

      ```python Python theme={null}
      import os, requests
      requests.post(
          "https://api.nuvia.ai/v1/webhooks",
          headers={"Authorization": f"Bearer {os.environ['NUVIA_API_KEY']}"},
          json={
              "url": "https://your-server.com/nuvia/webhook",
              "events": ["MESSAGE_RECEIVED", "CONVERSATION_CREATED"],
              "status": "ACTIVE",
          },
      )
      ```
    </CodeGroup>

    Optional fields: `custom_headers` (for you to validate the origin) and `status` (`ACTIVE`/`INACTIVE`).
  </Step>

  <Step title="Receive the callbacks">
    When an event occurs, Nuvia makes a `POST` to your `url`, with the header
    `X-Webhook-Event` and a three-field body:

    ```json theme={null}
    {
      "event": "MESSAGE_RECEIVED",
      "timestamp": "2026-08-13T14:03:00.000Z",
      "data": {}
    }
    ```

    `data` carries the event's resource: the message, the conversation, or the contact, in the
    same format as the corresponding `GET`. `FIELD_UPDATED` is the exception and has its own
    structure.

    <Warning>
      Respond `2xx` within **10 seconds** and process in the background. There's no retry: if
      your response fails or times out, the event is lost. Details in
      [Delivery guarantees](/en/guias/webhooks#delivery-guarantees).
    </Warning>
  </Step>

  <Step title="React to the event">
    From the callback, you can act via API: reply with
    `POST /v1/messages/send` (Case 1) or download attachments with
    `GET /v1/messages/conversation/:id/attachments`.
  </Step>

  <Step title="Manage the configuration">
    `GET /v1/webhooks` returns **one object** with the company's configuration (or `null`). It's
    not a list. Use that object's `id` to update with `PUT /v1/webhooks/:id` or remove with
    `DELETE /v1/webhooks/:id`. To pause without losing the configuration, prefer `PUT` with
    `status: "INACTIVE"`.
  </Step>
</Steps>

## Case 3: Campaigns and bulk sending

**Scopes:** `campaigns:create`, `campaigns:read`, `campaigns:update`, `campaigns:delete`
(+ `messages:create` for the bulk template shortcut).

<Steps>
  <Step title="Create the campaign">
    `POST /v1/campaigns`. Required fields: `name`, `channel_type`, `trigger_type`, and
    `audience_config`; optional ones include `description`, `trigger_config`, `behavior_config`, and
    `sender_config`. The internal structures of these configuration objects are in the
    [API reference](/en/api-reference/campaigns). Follow the schema there.

    The **audience is defined at creation time**, inside `audience_config` (via `contact_ids`,
    `lists`, or `filters`). There's no separate enrollment step to do via API. The campaign is
    already born with the audience you provide here.
  </Step>

  <Step title="Check before sending">
    Use the previews (they create nothing): `POST /v1/campaigns/audience-preview` counts the
    audience's contacts and `POST /v1/campaigns/preview-message` resolves the message variables.
  </Step>

  <Step title="Activate and track">
    Activate with `POST /v1/campaigns/:id/activate` (pause with `/pause`). Track with
    `GET /v1/campaigns/:id/analytics` and `GET /v1/campaigns/:id/step-analytics`, and export the
    contacts with `GET /v1/campaigns/:id/enrollments/export`.

    ```bash cURL theme={null}
    curl -X POST https://api.nuvia.ai/v1/campaigns/CAMPAIGN_ID/activate \
      -H "Authorization: Bearer $NUVIA_API_KEY"
    ```
  </Step>
</Steps>

<Note>
  **Shortcut:** to send a WhatsApp template to several contacts without building a campaign, use
  `POST /v1/messages/send-bulk-template` (scope `messages:create`). The step-by-step, with the
  template parameters and Meta's limits, is in the guide
  [Bulk template sending on WhatsApp](/en/guias/whatsapp-template-massa).
</Note>

## Case 4: Sync contacts

Keep your external CRM and Nuvia in sync.

**Scopes:** `contacts:read`, `contacts:create`, `contacts:update`, `contacts:delete`.

<Steps>
  <Step title="Check for duplicates by phone">
    Before creating, resolve the phone numbers to see which already exist: `POST /v1/contacts/find-by-phones`.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X POST https://api.nuvia.ai/v1/contacts/find-by-phones \
        -H "Authorization: Bearer $NUVIA_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{ "phones": ["+5511999999999", "+5511888888888"] }'
      ```

      ```javascript JavaScript theme={null}
      const res = await fetch("https://api.nuvia.ai/v1/contacts/find-by-phones", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.NUVIA_API_KEY}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ phones: ["+5511999999999", "+5511888888888"] }),
      });
      ```

      ```python Python theme={null}
      import os, requests
      res = requests.post(
          "https://api.nuvia.ai/v1/contacts/find-by-phones",
          headers={"Authorization": f"Bearer {os.environ['NUVIA_API_KEY']}"},
          json={"phones": ["+5511999999999", "+5511888888888"]},
      )
      ```
    </CodeGroup>
  </Step>

  <Step title="Create the missing contacts">
    `POST /v1/contacts`. Fields: `firstname`, `lastname`, `phone`, `email`, `company`, and `properties`.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X POST https://api.nuvia.ai/v1/contacts \
        -H "Authorization: Bearer $NUVIA_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "firstname": "Mary",
          "lastname": "Smith",
          "phone": "+5511999999999",
          "email": "mary@company.com"
        }'
      ```

      ```javascript JavaScript theme={null}
      await fetch("https://api.nuvia.ai/v1/contacts", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.NUVIA_API_KEY}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          firstname: "Mary",
          lastname: "Smith",
          phone: "+5511999999999",
          email: "mary@company.com",
        }),
      });
      ```

      ```python Python theme={null}
      import os, requests
      requests.post(
          "https://api.nuvia.ai/v1/contacts",
          headers={"Authorization": f"Bearer {os.environ['NUVIA_API_KEY']}"},
          json={
              "firstname": "Mary",
              "lastname": "Smith",
              "phone": "+5511999999999",
              "email": "mary@company.com",
          },
      )
      ```
    </CodeGroup>
  </Step>

  <Step title="Update and list">
    Update a contact with `PUT /v1/contacts/:id` (or just the name with `PUT /v1/contacts/:id/name`) and
    list/paginate with `GET /v1/contacts`. To **remove the link** between a contact and an external CRM,
    use `DELETE /v1/contacts/:id/crm-link`: this **only undoes the link; the contact keeps
    existing** in Nuvia (it's not a contact deletion).
  </Step>
</Steps>

## Other public surfaces

Consumable via API key and covered in the Reference: **Agents**, **Tables and lists**, **Conversations**, and
**Knowledge base**. See the map in [API sections](/en/secoes-da-api).

## Next steps

<CardGroup cols={2}>
  <Card title="API Key guide" icon="key" href="/en/guia-api-key">
    Scopes, key creation, and revocation.
  </Card>

  <Card title="API reference" icon="code" href="/en/api-reference">
    All endpoints, parameters, and responses.
  </Card>
</CardGroup>
