# conversation.started

Source: https://docs.revoplyai.com/webhooks/events/conversation-started/

> A new conversation began.

Webhook event `conversation.started`, delivered as `POST` to your endpoint.

**Fires when** the first time a customer writes on a channel and a conversation is created for them, and when a flow's hook starts a conversation with a number that had none. Once per conversation.

**Does not fire** when a customer writes again in a conversation that already exists, even one resolved long ago — that is `message.received`. Not when a conversation is reopened or reassigned.

## Parameters

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `X-Revoply-Signature` | header | yes | `t=<unix seconds>,v1=<hex HMAC-SHA256 of "{t}.{body}">`, with a second `v1` while a rotated secret still signs. Verify it before trusting the body. |
| `X-Revoply-Event` | header | yes | The event's `type`, so a receiver can route before parsing. |
| `X-Revoply-Delivery` | header | yes | This delivery's id. A resend of the same event keeps the envelope `id` and gets a new delivery id. |

## Payload

```json
{
  "allOf": [
    {
      "required": [
        "id",
        "type",
        "apiVersion",
        "createdAt",
        "test",
        "company",
        "data"
      ],
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "description": "The event's id, `evt_…`. The same on every retry and resend.",
          "example": "evt_4e1b9c2d7a3f4e8b9c0d1e2f3a4b5c6d"
        },
        "type": {
          "type": "string",
          "description": "What happened. Treat an unknown value as an event you did not subscribe to, and acknowledge it.\n\nKnown values: `message.received`, `conversation.started`, `conversation.handover`, `conversation.assigned`, `conversation.resolved`, `contact.created`, `contact.updated`, `contact.opted_out`, `lead.qualified`, `appointment.booked`, `appointment.cancelled`, `flow.completed`, `flow.failed`, `ping`. More may be added.",
          "x-extensible-enum": [
            "message.received",
            "conversation.started",
            "conversation.handover",
            "conversation.assigned",
            "conversation.resolved",
            "contact.created",
            "contact.updated",
            "contact.opted_out",
            "lead.qualified",
            "appointment.booked",
            "appointment.cancelled",
            "flow.completed",
            "flow.failed",
            "ping"
          ]
        },
        "apiVersion": {
          "type": "string",
          "description": "The shape of `data`. A new version is opt-in per endpoint.",
          "example": "2026-10"
        },
        "createdAt": {
          "type": "string",
          "description": "When it happened, UTC.",
          "format": "date-time"
        },
        "test": {
          "type": "boolean",
          "description": "True for what \"Send test\" posts: a sample, about nobody real. Always present."
        },
        "company": {
          "required": [
            "id",
            "name"
          ],
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "description": "The account (or project) it happened in.",
              "format": "uuid"
            },
            "name": {
              "type": [
                "null",
                "string"
              ]
            }
          }
        },
        "data": {
          "type": "object",
          "description": "Depends on `type`; see each event."
        }
      },
      "description": "Every event arrives in this envelope. Fields are only ever added, and a field that can be empty is sent as null rather than left out. Deduplicate on `id`: a delivery can arrive more than once."
    },
    {
      "type": "object",
      "properties": {
        "type": {
          "const": "conversation.started"
        },
        "data": {
          "required": [
            "conversation",
            "contact"
          ],
          "type": "object",
          "properties": {
            "conversation": {
              "required": [
                "id",
                "channelId",
                "channelType"
              ],
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "description": "The conversation's id. Opaque; may contain `_`, `.`, `+`, `=` and `@`."
                },
                "channelId": {
                  "type": [
                    "null",
                    "string"
                  ],
                  "description": "The channel (number, page, bot or widget) it is on."
                },
                "channelType": {
                  "type": [
                    "null",
                    "string"
                  ],
                  "description": "`whatsapp`, `whatsapp_qr`, `telegram`, `messenger`, `instagram`, `web_widget`… Treat an unknown value as another channel.\n\nKnown values: `whatsapp`, `whatsapp_qr`, `telegram`, `messenger`, `instagram`, `web_widget`, `website`, `voice`. More may be added.",
                  "x-extensible-enum": [
                    "whatsapp",
                    "whatsapp_qr",
                    "telegram",
                    "messenger",
                    "instagram",
                    "web_widget",
                    "website",
                    "voice"
                  ]
                }
              }
            },
            "contact": {
              "required": [
                "id",
                "name",
                "phone",
                "whatsAppId"
              ],
              "type": [
                "null",
                "object"
              ],
              "properties": {
                "id": {
                  "type": [
                    "null",
                    "string"
                  ],
                  "description": "The contact's id; null for a web widget visitor."
                },
                "name": {
                  "type": [
                    "null",
                    "string"
                  ]
                },
                "phone": {
                  "type": [
                    "null",
                    "string"
                  ],
                  "description": "E.164, such as `+966501234567`, or null — never a WhatsApp user id."
                },
                "whatsAppId": {
                  "type": [
                    "null",
                    "string"
                  ],
                  "description": "On WhatsApp, the id WhatsApp knows the customer by: the number without `+`, or a business-scoped user id when WhatsApp withholds the number. Null on other channels."
                }
              }
            }
          }
        }
      }
    }
  ]
}
```

Example:

```json
{
  "id": "evt_97b319133f7563d685e9cbbbac6968cd",
  "type": "conversation.started",
  "apiVersion": "2026-10",
  "createdAt": "2026-10-01T09:30:05+00:00",
  "test": false,
  "company": {
    "id": "0b3d6f2e-8a41-4c75-9e20-5f7a1c3d9b84",
    "name": "متجر الرياض"
  },
  "data": {
    "conversation": {
      "id": "WhatsApp_3f6a2d18-0b7c-4e91-8a5d-c24e1f9b7a60",
      "channelId": "7c1e4b2a-9d3f-4e58-a6b1-2f0c8d7e5a13",
      "channelType": "whatsapp"
    },
    "contact": {
      "id": "3f6a2d18-0b7c-4e91-8a5d-c24e1f9b7a60",
      "name": "نورة العتيبي",
      "phone": "+966501234567",
      "whatsAppId": "966501234567"
    }
  }
}
```

## Responses

### 410

The endpoint is gone for good: it is switched off at once, and the account's owner is told.

### 2XX

Received. Answer within 10 seconds and do the work afterwards; the body is kept for your delivery log and otherwise ignored.

### default

Anything else, a timeout or no answer is a failure, tried again after 1 min, 5 min, 30 min, 2 h, 6 h, 12 h, 24 h, then dead-lettered. An endpoint failing 100 times in a row, or for 3 days, is switched off. Tests are never retried.
