# contact.updated

Source: https://docs.revoplyai.com/webhooks/events/contact-updated/

> A contact's details changed.

Webhook event `contact.updated`, delivered as `POST` to your endpoint.

**Fires when** a team member edits a contact's name, identifiers, tags or custom fields, a flow's step saves a field or a tag to it, or a contact who had opted out is opted back in.

**Does not fire** when a contact merely writes again: the last-seen time is not news. Not for imports. Opting out is `contact.opted_out` instead.

## 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": "contact.updated"
        },
        "data": {
          "required": [
            "contact"
          ],
          "type": "object",
          "properties": {
            "contact": {
              "required": [
                "id",
                "name",
                "phone",
                "whatsAppId",
                "email",
                "language",
                "tags",
                "fields",
                "optedOut"
              ],
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "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": "The WhatsApp user id the contact is filed under — a business-scoped id or an `@lid` — as stored; null when only a number is known."
                },
                "email": {
                  "type": [
                    "null",
                    "string"
                  ]
                },
                "language": {
                  "type": [
                    "null",
                    "string"
                  ],
                  "description": "`ar`, `en`… The language the customer writes in, when known."
                },
                "tags": {
                  "type": "array",
                  "items": {
                    "type": [
                      "null",
                      "string"
                    ]
                  }
                },
                "fields": {
                  "type": "object",
                  "additionalProperties": {
                    "type": [
                      "null",
                      "string"
                    ]
                  },
                  "description": "The contact's custom fields, by key."
                },
                "optedOut": {
                  "type": "boolean"
                }
              }
            }
          }
        }
      }
    }
  ]
}
```

Example:

```json
{
  "id": "evt_718f639779077f75cdfaa77247a5da61",
  "type": "contact.updated",
  "apiVersion": "2026-10",
  "createdAt": "2026-10-01T09:30:05+00:00",
  "test": false,
  "company": {
    "id": "0b3d6f2e-8a41-4c75-9e20-5f7a1c3d9b84",
    "name": "متجر الرياض"
  },
  "data": {
    "contact": {
      "id": "3f6a2d18-0b7c-4e91-8a5d-c24e1f9b7a60",
      "name": "نورة العتيبي",
      "phone": "+966501234567",
      "whatsAppId": null,
      "email": "noura@example.com",
      "language": "ar",
      "tags": [
        "whatsapp",
        "عميل مميز"
      ],
      "fields": {
        "city": "الرياض",
        "orderCount": "4"
      },
      "optedOut": false
    }
  }
}
```

## 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.
