{
  "openapi": "3.1.1",
  "info": {
    "title": "RevoplyAI API",
    "description": "RevoplyAI's API for merchants' own systems, on the Business plan: the hooks that start an automation from outside, and the webhook events we post to your endpoints. Guides, recipes and the changelog are at https://docs.revoplyai.com.",
    "version": "1.0"
  },
  "servers": [
    {
      "url": "https://api.revoplyai.com",
      "description": "Production"
    }
  ],
  "paths": {
    "/hooks/{token}": {
      "post": {
        "tags": [
          "Hooks"
        ],
        "summary": "Start an automation",
        "description": "Starts the flow this hook belongs to for the customer your JSON names, by the paths set in the flow's trigger. The run is queued, not finished: `202 accepted` returns its id at once, and `flow.completed` or `flow.failed` tells you how it ended. Send an `Idempotency-Key` to make retries safe.",
        "operationId": "StartFlowFromHook",
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "description": "The hook's token, as the flow builder shows it once: `rvh_` and 43 URL-safe characters. Keep it secret; regenerate it if it leaks.",
            "required": true,
            "schema": {
              "pattern": "^rvh_[A-Za-z0-9_-]{37,95}$",
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Your own id for this request, such as the order number. A repeat with the same key starts nothing new and answers 200 `duplicate` with the first run's id.",
            "schema": {
              "maxLength": 200,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "Any JSON object up to 64 KB. The flow's trigger settings name the paths its phone number, name, language and saved values are read from.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              },
              "example": {
                "order": {
                  "id": "10482",
                  "status": "shipped"
                },
                "customer": {
                  "name": "نورة العتيبي",
                  "phone": "+966501234567"
                }
              }
            }
          },
          "required": true,
          "x-revoply-max-bytes": 65536
        },
        "responses": {
          "200": {
            "description": "`duplicate`: A request with this `Idempotency-Key` already started a run; nothing new was queued. `id` is that run.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HookResponse"
                },
                "examples": {
                  "duplicate": {
                    "summary": "A request with this `Idempotency-Key` already started a run; nothing new was queued. `id` is that run.",
                    "value": {
                      "id": "8f14e45f-ceea-467a-9575-5b1e2c7d3a90",
                      "status": "duplicate",
                      "detail": null
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "`accepted`: The flow was queued for the customer the body names. `id` is the run.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HookResponse"
                },
                "examples": {
                  "accepted": {
                    "summary": "The flow was queued for the customer the body names. `id` is the run.",
                    "value": {
                      "id": "8f14e45f-ceea-467a-9575-5b1e2c7d3a90",
                      "status": "accepted",
                      "detail": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`invalid_json`: The body is not JSON. `idempotency_key_too_long`: The `Idempotency-Key` header is longer than 200 characters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HookResponse"
                },
                "examples": {
                  "invalid_json": {
                    "summary": "The body is not JSON.",
                    "value": {
                      "id": null,
                      "status": "invalid_json",
                      "detail": null
                    }
                  },
                  "idempotency_key_too_long": {
                    "summary": "The `Idempotency-Key` header is longer than 200 characters.",
                    "value": {
                      "id": null,
                      "status": "idempotency_key_too_long",
                      "detail": null
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "`not_in_plan`: The account's plan does not include automations, the developer API or automated templates.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HookResponse"
                },
                "examples": {
                  "not_in_plan": {
                    "summary": "The account's plan does not include automations, the developer API or automated templates.",
                    "value": {
                      "id": null,
                      "status": "not_in_plan",
                      "detail": null
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`not_found`: No hook has this token. It may have been regenerated or deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HookResponse"
                },
                "examples": {
                  "not_found": {
                    "summary": "No hook has this token. It may have been regenerated or deleted.",
                    "value": {
                      "id": null,
                      "status": "not_found",
                      "detail": null
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "`flow_off`: The flow is not published, is switched off, or is beyond the plan's number of flows. `flow_not_listening`: The published flow is not started by its webhook. `channel_unavailable`: The flow's WhatsApp number is not connected. `opted_out`: The customer opted out of messages from this business.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HookResponse"
                },
                "examples": {
                  "flow_off": {
                    "summary": "The flow is not published, is switched off, or is beyond the plan's number of flows.",
                    "value": {
                      "id": null,
                      "status": "flow_off",
                      "detail": "The flow is not published, switched on and within the plan's flows."
                    }
                  },
                  "flow_not_listening": {
                    "summary": "The published flow is not started by its webhook.",
                    "value": {
                      "id": null,
                      "status": "flow_not_listening",
                      "detail": "The published flow is not started by its webhook."
                    }
                  },
                  "channel_unavailable": {
                    "summary": "The flow's WhatsApp number is not connected.",
                    "value": {
                      "id": null,
                      "status": "channel_unavailable",
                      "detail": "The flow's WhatsApp number is not connected."
                    }
                  },
                  "opted_out": {
                    "summary": "The customer opted out of messages from this business.",
                    "value": {
                      "id": null,
                      "status": "opted_out",
                      "detail": "The customer opted out of messages."
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "`payload_too_large`: The body is over 64 KB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HookResponse"
                },
                "examples": {
                  "payload_too_large": {
                    "summary": "The body is over 64 KB.",
                    "value": {
                      "id": null,
                      "status": "payload_too_large",
                      "detail": "Keep the body under 64 KB."
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "`unsupported_media_type`: The body was not sent as `application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HookResponse"
                },
                "examples": {
                  "unsupported_media_type": {
                    "summary": "The body was not sent as `application/json`.",
                    "value": {
                      "id": null,
                      "status": "unsupported_media_type",
                      "detail": "Send the body as application/json."
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`invalid_phone`: No phone number at the flow's phone path, or one that cannot be read. Send it in E.164, such as +9665XXXXXXXX.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HookResponse"
                },
                "examples": {
                  "invalid_phone": {
                    "summary": "No phone number at the flow's phone path, or one that cannot be read. Send it in E.164, such as +9665XXXXXXXX.",
                    "value": {
                      "id": null,
                      "status": "invalid_phone",
                      "detail": "No phone number at \"customer.phone\"."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: More than 60 requests a minute to this hook, or 1000 an hour to the account's hooks together. `template_limit_reached`: Today's automated templates for the account are used up. `customer_limit_reached`: This customer has had 3 automated templates in the last 24 hours. Our per-address limit on hook URLs may also answer 429, as plain text.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HookResponse"
                },
                "examples": {
                  "rate_limited": {
                    "summary": "More than 60 requests a minute to this hook, or 1000 an hour to the account's hooks together.",
                    "value": {
                      "id": null,
                      "status": "rate_limited",
                      "detail": null
                    }
                  },
                  "template_limit_reached": {
                    "summary": "Today's automated templates for the account are used up.",
                    "value": {
                      "id": null,
                      "status": "template_limit_reached",
                      "detail": "Today's automated templates are used up."
                    }
                  },
                  "customer_limit_reached": {
                    "summary": "This customer has had 3 automated templates in the last 24 hours.",
                    "value": {
                      "id": null,
                      "status": "customer_limit_reached",
                      "detail": "This customer has had 3 automated templates in the last 24 hours."
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "`service_unavailable`: Hooks are paused on our side, or we could not count requests. Try again in a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HookResponse"
                },
                "examples": {
                  "service_unavailable": {
                    "summary": "Hooks are paused on our side, or we could not count requests. Try again in a minute.",
                    "value": {
                      "id": null,
                      "status": "service_unavailable",
                      "detail": "Try again in a minute."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [],
        "x-revoply-plan": "Business",
        "x-revoply-try-it": false
      },
      "servers": [
        {
          "url": "https://api.revoplyai.com",
          "description": "Hooks are called on the API's host, outside /v1, with no API key: the token in the URL is the credential."
        }
      ]
    }
  },
  "components": {
    "schemas": {
      "AppointmentData": {
        "required": [
          "appointment",
          "conversation",
          "contact"
        ],
        "type": "object",
        "properties": {
          "appointment": {
            "required": [
              "id",
              "reference",
              "service",
              "startsAt",
              "endsAt",
              "status",
              "customerName",
              "cancellationReason"
            ],
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "reference": {
                "type": [
                  "null",
                  "string"
                ]
              },
              "service": {
                "type": [
                  "null",
                  "string"
                ]
              },
              "startsAt": {
                "type": "string",
                "format": "date-time"
              },
              "endsAt": {
                "type": "string",
                "format": "date-time"
              },
              "status": {
                "type": "string",
                "description": "`confirmed`, `cancelled`, `no_show` or `completed`."
              },
              "customerName": {
                "type": [
                  "null",
                  "string"
                ]
              },
              "cancellationReason": {
                "type": [
                  "null",
                  "string"
                ]
              }
            }
          },
          "conversation": {
            "required": [
              "id",
              "channelId",
              "channelType"
            ],
            "type": [
              "null",
              "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.",
                "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."
              }
            }
          }
        }
      },
      "ContactData": {
        "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"
              }
            }
          }
        }
      },
      "ConversationAssignedData": {
        "required": [
          "conversation",
          "assignee"
        ],
        "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.",
                "x-extensible-enum": [
                  "whatsapp",
                  "whatsapp_qr",
                  "telegram",
                  "messenger",
                  "instagram",
                  "web_widget",
                  "website",
                  "voice"
                ]
              }
            }
          },
          "assignee": {
            "required": [
              "id",
              "name",
              "email"
            ],
            "type": [
              "null",
              "object"
            ],
            "properties": {
              "id": {
                "type": [
                  "null",
                  "string"
                ]
              },
              "name": {
                "type": [
                  "null",
                  "string"
                ]
              },
              "email": {
                "type": [
                  "null",
                  "string"
                ]
              }
            }
          }
        }
      },
      "ConversationData": {
        "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.",
                "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."
              }
            }
          }
        }
      },
      "ConversationHandoverData": {
        "required": [
          "conversation",
          "contact",
          "reason",
          "reasonText"
        ],
        "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.",
                "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."
              }
            }
          },
          "reason": {
            "type": "string",
            "description": "`customer_asked_for_person`, `missing_information`, `needs_staff_action`, `customer_upset`, `qualified_lead`, `flow_handover`, `message_burst`, `unsupported_media`… Treat an unknown value as `other`."
          },
          "reasonText": {
            "type": [
              "null",
              "string"
            ],
            "description": "The assistant's own words, when it gave any. For people; may be Arabic."
          }
        }
      },
      "FlowRunData": {
        "required": [
          "flow",
          "run",
          "conversation",
          "contact"
        ],
        "type": "object",
        "properties": {
          "flow": {
            "required": [
              "id",
              "name"
            ],
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "run": {
            "required": [
              "id",
              "status",
              "endReason",
              "triggerKind",
              "startedAt",
              "endedAt",
              "variables"
            ],
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "status": {
                "type": "string",
                "description": "`completed` or `failed`, as the event's type says."
              },
              "endReason": {
                "type": [
                  "null",
                  "string"
                ],
                "description": "Why it ended: `completed`, `handed_over`, `customer_exited`, `error`, `limit_exceeded`… Treat an unknown value as its status."
              },
              "triggerKind": {
                "type": "string",
                "description": "What started it: `webhook`, `keyword`, `conversation_started`, `manual`… May grow."
              },
              "startedAt": {
                "type": "string",
                "format": "date-time"
              },
              "endedAt": {
                "type": [
                  "null",
                  "string"
                ],
                "format": "date-time"
              },
              "variables": {
                "type": "object",
                "additionalProperties": {
                  "type": [
                    "null",
                    "string"
                  ]
                },
                "description": "What the run collected — the answers to its questions and the values it saved, as strings."
              }
            }
          },
          "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.",
                "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."
              }
            }
          }
        }
      },
      "HookResponse": {
        "required": [
          "id",
          "status",
          "detail"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": [
              "null",
              "string"
            ],
            "description": "The run the request started — or, for a repeat, the one it started the first time. Null when nothing was started.",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "description": "`accepted`, `duplicate`, or why not. Treat an unknown value as a failure you did not expect, and log it.",
            "x-extensible-enum": [
              "accepted",
              "duplicate",
              "invalid_json",
              "idempotency_key_too_long",
              "not_in_plan",
              "not_found",
              "flow_off",
              "flow_not_listening",
              "channel_unavailable",
              "opted_out",
              "payload_too_large",
              "unsupported_media_type",
              "invalid_phone",
              "rate_limited",
              "template_limit_reached",
              "customer_limit_reached",
              "service_unavailable"
            ]
          },
          "detail": {
            "type": [
              "null",
              "string"
            ],
            "description": "Why, in words for people. May change; branch on status."
          }
        },
        "description": "What a request to a hook came to. Branch on `status`; `detail` is for people."
      },
      "LeadQualifiedData": {
        "required": [
          "lead",
          "conversation"
        ],
        "type": "object",
        "properties": {
          "lead": {
            "required": [
              "id",
              "customerName",
              "status",
              "temperature",
              "score",
              "qualifiedAt"
            ],
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "customerName": {
                "type": [
                  "null",
                  "string"
                ]
              },
              "status": {
                "type": "string",
                "description": "`qualified` on this event."
              },
              "temperature": {
                "type": [
                  "null",
                  "string"
                ],
                "description": "`cold`, `warm` or `hot`."
              },
              "score": {
                "type": [
                  "null",
                  "integer"
                ],
                "description": "0 to 100."
              },
              "qualifiedAt": {
                "type": [
                  "null",
                  "string"
                ],
                "format": "date-time"
              }
            }
          },
          "conversation": {
            "required": [
              "id",
              "channelId",
              "channelType"
            ],
            "type": [
              "null",
              "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.",
                "x-extensible-enum": [
                  "whatsapp",
                  "whatsapp_qr",
                  "telegram",
                  "messenger",
                  "instagram",
                  "web_widget",
                  "website",
                  "voice"
                ]
              }
            }
          }
        }
      },
      "MessageReceivedData": {
        "required": [
          "conversation",
          "contact",
          "message"
        ],
        "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.",
                "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."
              }
            }
          },
          "message": {
            "required": [
              "id",
              "text",
              "type",
              "receivedAt"
            ],
            "type": "object",
            "properties": {
              "id": {
                "type": [
                  "null",
                  "string"
                ]
              },
              "text": {
                "type": [
                  "null",
                  "string"
                ],
                "description": "The text, or a media message's caption; cut at 4,096 characters."
              },
              "type": {
                "type": "string",
                "description": "`text`, `image`, `audio`, `video`, `document`, `sticker`, `location`, `contact`… Treat an unknown value as a message you cannot read."
              },
              "receivedAt": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "PingData": {
        "required": [
          "message"
        ],
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          }
        }
      },
      "Problem": {
        "required": [
          "type",
          "title",
          "status",
          "detail",
          "code",
          "requestId",
          "errors"
        ],
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "The code's entry in the errors guide.",
            "format": "uri",
            "example": "https://docs.revoplyai.com/get-started/errors/#invalid_phone"
          },
          "title": {
            "type": "string",
            "example": "The phone number cannot be read"
          },
          "status": {
            "type": "integer",
            "format": "int32",
            "example": 422
          },
          "detail": {
            "type": [
              "null",
              "string"
            ],
            "example": "Send the number with its country code, such as +9665XXXXXXXX."
          },
          "code": {
            "type": "string",
            "description": "From the closed list in the errors guide.",
            "example": "invalid_phone"
          },
          "requestId": {
            "type": "string",
            "description": "Quote it to support.",
            "example": "0HN7Q3JH1QK5V:00000001"
          },
          "errors": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/ProblemError"
            },
            "description": "What to fix, field by field, when there is more than one thing."
          }
        },
        "description": "An RFC 9457 problem, as every `/v1` error is answered (`application/problem+json`). Branch on `code`, never on `title` or `detail`. The flow hooks answer with `HookResponse` instead."
      },
      "ProblemError": {
        "required": [
          "field",
          "code",
          "message"
        ],
        "type": "object",
        "properties": {
          "field": {
            "type": "string",
            "description": "A dot path into the request, such as `template.parameters.body[1]`.",
            "example": "phone"
          },
          "code": {
            "type": "string",
            "description": "What is wrong with it. Branch on this.",
            "example": "invalid_phone"
          },
          "message": {
            "type": "string",
            "description": "For people; may change.",
            "example": "Send the number with its country code, such as +9665XXXXXXXX."
          }
        },
        "description": "One thing wrong with the request."
      },
      "WebhookEnvelope": {
        "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.",
            "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."
      }
    },
    "securitySchemes": {
      "ApiKey": {
        "type": "http",
        "description": "An API key from Integrations → API keys, sent as `Authorization: Bearer rvp_live_…`.",
        "scheme": "bearer",
        "bearerFormat": "rvp_live_…"
      }
    }
  },
  "tags": [
    {
      "name": "Hooks",
      "description": "Start one of your automations from your own system — an order shipped, a form filled in."
    },
    {
      "name": "Webhook events",
      "description": "What we post to your endpoints when something happens. Every request is signed; see the signature guide."
    }
  ],
  "x-revoply-plan": "Business",
  "x-tagGroups": [
    {
      "name": "Automations",
      "tags": [
        "Hooks"
      ]
    },
    {
      "name": "Webhooks",
      "tags": [
        "Webhook events"
      ]
    }
  ],
  "webhooks": {
    "message.received": {
      "post": {
        "tags": [
          "Webhook events"
        ],
        "summary": "A customer wrote.",
        "description": "**Fires when** a customer's message is saved to a conversation on any channel — WhatsApp (Cloud and QR), Telegram, Messenger, Instagram or the web widget — whoever is handling the conversation at the time. Once per message: a provider delivering the same message twice raises it once. Media arrives with its `type` (`image`, `audio`, `document`…) and any caption as `text`.\n\n**Does not fire** for anything the business sends: the assistant's replies, a flow's messages, your team's replies from the inbox, and the owner's replies from the WhatsApp Business app on a coexistence number. Not for the history WhatsApp imports when a coexistence number is connected. Not for delivery or read receipts.",
        "operationId": "webhook_message_received",
        "parameters": [
          {
            "name": "X-Revoply-Signature",
            "in": "header",
            "description": "`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.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "t=1790847005,v1=3f1c9a…"
          },
          {
            "name": "X-Revoply-Event",
            "in": "header",
            "description": "The event's `type`, so a receiver can route before parsing.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "message.received"
          },
          {
            "name": "X-Revoply-Delivery",
            "in": "header",
            "description": "This delivery's id. A resend of the same event keeps the envelope `id` and gets a new delivery id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "5a9e2c7b-1f34-4d86-b0a5-3c8e7d2f1b69"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "message.received"
                      },
                      "data": {
                        "$ref": "#/components/schemas/MessageReceivedData"
                      }
                    }
                  }
                ]
              },
              "example": {
                "id": "evt_5dabddce7c097ab703f21e8fdf0ab52b",
                "type": "message.received",
                "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"
                  },
                  "message": {
                    "id": "m_wamid.HBgMOTY2NTAxMjM0NTY3FQIAEhggQTNFQjU2RkQ5RTcyOEIyRDQ1",
                    "text": "السلام عليكم، متى يوصل طلبي رقم 10482؟",
                    "type": "text",
                    "receivedAt": "2026-10-01T09:30:00+00:00"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "410": {
            "description": "The endpoint is gone for good: it is switched off at once, and the account's owner is told."
          },
          "2XX": {
            "description": "Received. Answer within 10 seconds and do the work afterwards; the body is kept for your delivery log and otherwise ignored."
          },
          "default": {
            "description": "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."
          }
        },
        "security": []
      }
    },
    "conversation.started": {
      "post": {
        "tags": [
          "Webhook events"
        ],
        "summary": "A new conversation began.",
        "description": "**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.\n\n**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.",
        "operationId": "webhook_conversation_started",
        "parameters": [
          {
            "name": "X-Revoply-Signature",
            "in": "header",
            "description": "`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.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "t=1790847005,v1=3f1c9a…"
          },
          {
            "name": "X-Revoply-Event",
            "in": "header",
            "description": "The event's `type`, so a receiver can route before parsing.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "conversation.started"
          },
          {
            "name": "X-Revoply-Delivery",
            "in": "header",
            "description": "This delivery's id. A resend of the same event keeps the envelope `id` and gets a new delivery id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "5a9e2c7b-1f34-4d86-b0a5-3c8e7d2f1b69"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "conversation.started"
                      },
                      "data": {
                        "$ref": "#/components/schemas/ConversationData"
                      }
                    }
                  }
                ]
              },
              "example": {
                "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"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "410": {
            "description": "The endpoint is gone for good: it is switched off at once, and the account's owner is told."
          },
          "2XX": {
            "description": "Received. Answer within 10 seconds and do the work afterwards; the body is kept for your delivery log and otherwise ignored."
          },
          "default": {
            "description": "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."
          }
        },
        "security": []
      }
    },
    "conversation.handover": {
      "post": {
        "tags": [
          "Webhook events"
        ],
        "summary": "The conversation was handed to the team.",
        "description": "**Fires when** the assistant or the platform hands a conversation to a person: the customer asked for one, the assistant could not answer, the customer is upset, a flow reached a handover step, a lead qualified for handoff, or loop protection stepped in. `reason` says which, as a stable code; `reasonText` is the assistant's own words when it gave any.\n\n**Does not fire** again while the same handover is still open — a customer who asks twice raises it once. Not when a team member simply replies or is assigned; that is `conversation.assigned`.",
        "operationId": "webhook_conversation_handover",
        "parameters": [
          {
            "name": "X-Revoply-Signature",
            "in": "header",
            "description": "`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.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "t=1790847005,v1=3f1c9a…"
          },
          {
            "name": "X-Revoply-Event",
            "in": "header",
            "description": "The event's `type`, so a receiver can route before parsing.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "conversation.handover"
          },
          {
            "name": "X-Revoply-Delivery",
            "in": "header",
            "description": "This delivery's id. A resend of the same event keeps the envelope `id` and gets a new delivery id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "5a9e2c7b-1f34-4d86-b0a5-3c8e7d2f1b69"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "conversation.handover"
                      },
                      "data": {
                        "$ref": "#/components/schemas/ConversationHandoverData"
                      }
                    }
                  }
                ]
              },
              "example": {
                "id": "evt_cd92ffc66b8593f1dda08ca5c42ab051",
                "type": "conversation.handover",
                "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": null,
                    "phone": "+966501234567",
                    "whatsAppId": "966501234567"
                  },
                  "reason": "customer_asked_for_person",
                  "reasonText": "العميلة تطلب التحدث مع موظف بخصوص استرجاع المبلغ"
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "410": {
            "description": "The endpoint is gone for good: it is switched off at once, and the account's owner is told."
          },
          "2XX": {
            "description": "Received. Answer within 10 seconds and do the work afterwards; the body is kept for your delivery log and otherwise ignored."
          },
          "default": {
            "description": "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."
          }
        },
        "security": []
      }
    },
    "conversation.assigned": {
      "post": {
        "tags": [
          "Webhook events"
        ],
        "summary": "The conversation was assigned, or unassigned.",
        "description": "**Fires when** a team member assigns the conversation to someone, reassigns it, or clears the assignee (then `assignee` is null).\n\n**Does not fire** when the assignee does not change. Not for handovers to the team as a whole; that is `conversation.handover`.",
        "operationId": "webhook_conversation_assigned",
        "parameters": [
          {
            "name": "X-Revoply-Signature",
            "in": "header",
            "description": "`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.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "t=1790847005,v1=3f1c9a…"
          },
          {
            "name": "X-Revoply-Event",
            "in": "header",
            "description": "The event's `type`, so a receiver can route before parsing.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "conversation.assigned"
          },
          {
            "name": "X-Revoply-Delivery",
            "in": "header",
            "description": "This delivery's id. A resend of the same event keeps the envelope `id` and gets a new delivery id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "5a9e2c7b-1f34-4d86-b0a5-3c8e7d2f1b69"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "conversation.assigned"
                      },
                      "data": {
                        "$ref": "#/components/schemas/ConversationAssignedData"
                      }
                    }
                  }
                ]
              },
              "example": {
                "id": "evt_724f47a1bc59c78b8c9dd93b2f0fe1ba",
                "type": "conversation.assigned",
                "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"
                  },
                  "assignee": {
                    "id": "a91d3c5e-7f20-4b86-9e14-6d8b2c0f3a57",
                    "name": "خالد الشهري",
                    "email": "khaled@example.com"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "410": {
            "description": "The endpoint is gone for good: it is switched off at once, and the account's owner is told."
          },
          "2XX": {
            "description": "Received. Answer within 10 seconds and do the work afterwards; the body is kept for your delivery log and otherwise ignored."
          },
          "default": {
            "description": "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."
          }
        },
        "security": []
      }
    },
    "conversation.resolved": {
      "post": {
        "tags": [
          "Webhook events"
        ],
        "summary": "The conversation was resolved or closed.",
        "description": "**Fires when** a team member marks a conversation resolved or closed, one at a time or in bulk. A bulk close names each conversation by its `id` alone: `channelId`, `channelType` and `contact` are null.\n\n**Does not fire** when a conversation merely goes quiet, and when the assistant finishes answering — conversations are only resolved by people.",
        "operationId": "webhook_conversation_resolved",
        "parameters": [
          {
            "name": "X-Revoply-Signature",
            "in": "header",
            "description": "`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.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "t=1790847005,v1=3f1c9a…"
          },
          {
            "name": "X-Revoply-Event",
            "in": "header",
            "description": "The event's `type`, so a receiver can route before parsing.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "conversation.resolved"
          },
          {
            "name": "X-Revoply-Delivery",
            "in": "header",
            "description": "This delivery's id. A resend of the same event keeps the envelope `id` and gets a new delivery id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "5a9e2c7b-1f34-4d86-b0a5-3c8e7d2f1b69"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "conversation.resolved"
                      },
                      "data": {
                        "$ref": "#/components/schemas/ConversationData"
                      }
                    }
                  }
                ]
              },
              "example": {
                "id": "evt_3d224cab9d71622c12be691fefd09422",
                "type": "conversation.resolved",
                "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"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "410": {
            "description": "The endpoint is gone for good: it is switched off at once, and the account's owner is told."
          },
          "2XX": {
            "description": "Received. Answer within 10 seconds and do the work afterwards; the body is kept for your delivery log and otherwise ignored."
          },
          "default": {
            "description": "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."
          }
        },
        "security": []
      }
    },
    "contact.created": {
      "post": {
        "tags": [
          "Webhook events"
        ],
        "summary": "A contact was added.",
        "description": "**Fires when** someone writes for the first time on any channel that identifies them, a team member adds a contact by hand, or a flow's hook names a number that was not yet a contact.\n\n**Does not fire** for contacts brought in by a CSV or WhatsApp import — one click would be thousands of events. Not for web widget visitors, who have no contact.",
        "operationId": "webhook_contact_created",
        "parameters": [
          {
            "name": "X-Revoply-Signature",
            "in": "header",
            "description": "`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.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "t=1790847005,v1=3f1c9a…"
          },
          {
            "name": "X-Revoply-Event",
            "in": "header",
            "description": "The event's `type`, so a receiver can route before parsing.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "contact.created"
          },
          {
            "name": "X-Revoply-Delivery",
            "in": "header",
            "description": "This delivery's id. A resend of the same event keeps the envelope `id` and gets a new delivery id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "5a9e2c7b-1f34-4d86-b0a5-3c8e7d2f1b69"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "contact.created"
                      },
                      "data": {
                        "$ref": "#/components/schemas/ContactData"
                      }
                    }
                  }
                ]
              },
              "example": {
                "id": "evt_0034e332cb30bcb521b7a16de0148ed3",
                "type": "contact.created",
                "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
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "410": {
            "description": "The endpoint is gone for good: it is switched off at once, and the account's owner is told."
          },
          "2XX": {
            "description": "Received. Answer within 10 seconds and do the work afterwards; the body is kept for your delivery log and otherwise ignored."
          },
          "default": {
            "description": "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."
          }
        },
        "security": []
      }
    },
    "contact.updated": {
      "post": {
        "tags": [
          "Webhook events"
        ],
        "summary": "A contact's details changed.",
        "description": "**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.\n\n**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.",
        "operationId": "webhook_contact_updated",
        "parameters": [
          {
            "name": "X-Revoply-Signature",
            "in": "header",
            "description": "`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.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "t=1790847005,v1=3f1c9a…"
          },
          {
            "name": "X-Revoply-Event",
            "in": "header",
            "description": "The event's `type`, so a receiver can route before parsing.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "contact.updated"
          },
          {
            "name": "X-Revoply-Delivery",
            "in": "header",
            "description": "This delivery's id. A resend of the same event keeps the envelope `id` and gets a new delivery id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "5a9e2c7b-1f34-4d86-b0a5-3c8e7d2f1b69"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "contact.updated"
                      },
                      "data": {
                        "$ref": "#/components/schemas/ContactData"
                      }
                    }
                  }
                ]
              },
              "example": {
                "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
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "410": {
            "description": "The endpoint is gone for good: it is switched off at once, and the account's owner is told."
          },
          "2XX": {
            "description": "Received. Answer within 10 seconds and do the work afterwards; the body is kept for your delivery log and otherwise ignored."
          },
          "default": {
            "description": "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."
          }
        },
        "security": []
      }
    },
    "contact.opted_out": {
      "post": {
        "tags": [
          "Webhook events"
        ],
        "summary": "A contact opted out of messages.",
        "description": "**Fires when** the customer sends an opt-out keyword, stops broadcasts, or a team member marks them opted out.\n\n**Does not fire** when they are already opted out. Opting back in is `contact.updated` with `optedOut: false`.",
        "operationId": "webhook_contact_opted_out",
        "parameters": [
          {
            "name": "X-Revoply-Signature",
            "in": "header",
            "description": "`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.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "t=1790847005,v1=3f1c9a…"
          },
          {
            "name": "X-Revoply-Event",
            "in": "header",
            "description": "The event's `type`, so a receiver can route before parsing.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "contact.opted_out"
          },
          {
            "name": "X-Revoply-Delivery",
            "in": "header",
            "description": "This delivery's id. A resend of the same event keeps the envelope `id` and gets a new delivery id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "5a9e2c7b-1f34-4d86-b0a5-3c8e7d2f1b69"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "contact.opted_out"
                      },
                      "data": {
                        "$ref": "#/components/schemas/ContactData"
                      }
                    }
                  }
                ]
              },
              "example": {
                "id": "evt_fa7add3890a70571c215b5a65216015a",
                "type": "contact.opted_out",
                "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": true
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "410": {
            "description": "The endpoint is gone for good: it is switched off at once, and the account's owner is told."
          },
          "2XX": {
            "description": "Received. Answer within 10 seconds and do the work afterwards; the body is kept for your delivery log and otherwise ignored."
          },
          "default": {
            "description": "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."
          }
        },
        "security": []
      }
    },
    "lead.qualified": {
      "post": {
        "tags": [
          "Webhook events"
        ],
        "summary": "A conversation became a qualified lead.",
        "description": "**Fires when** lead qualification scores a conversation as qualified for the first time.\n\n**Does not fire** when a lead that is already qualified changes score or temperature, or when a lead is created but not yet qualified.",
        "operationId": "webhook_lead_qualified",
        "parameters": [
          {
            "name": "X-Revoply-Signature",
            "in": "header",
            "description": "`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.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "t=1790847005,v1=3f1c9a…"
          },
          {
            "name": "X-Revoply-Event",
            "in": "header",
            "description": "The event's `type`, so a receiver can route before parsing.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "lead.qualified"
          },
          {
            "name": "X-Revoply-Delivery",
            "in": "header",
            "description": "This delivery's id. A resend of the same event keeps the envelope `id` and gets a new delivery id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "5a9e2c7b-1f34-4d86-b0a5-3c8e7d2f1b69"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "lead.qualified"
                      },
                      "data": {
                        "$ref": "#/components/schemas/LeadQualifiedData"
                      }
                    }
                  }
                ]
              },
              "example": {
                "id": "evt_bc2246d8dc5862d6aa21fcb98cee0b24",
                "type": "lead.qualified",
                "apiVersion": "2026-10",
                "createdAt": "2026-10-01T09:30:05+00:00",
                "test": false,
                "company": {
                  "id": "0b3d6f2e-8a41-4c75-9e20-5f7a1c3d9b84",
                  "name": "متجر الرياض"
                },
                "data": {
                  "lead": {
                    "id": "5d2b8e14-6a9f-4c37-b0e5-1f7d3a9c2e48",
                    "customerName": "نورة العتيبي",
                    "status": "qualified",
                    "temperature": "hot",
                    "score": 86,
                    "qualifiedAt": "2026-10-01T09:30:00+00:00"
                  },
                  "conversation": {
                    "id": "WhatsApp_3f6a2d18-0b7c-4e91-8a5d-c24e1f9b7a60",
                    "channelId": "7c1e4b2a-9d3f-4e58-a6b1-2f0c8d7e5a13",
                    "channelType": "whatsapp"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "410": {
            "description": "The endpoint is gone for good: it is switched off at once, and the account's owner is told."
          },
          "2XX": {
            "description": "Received. Answer within 10 seconds and do the work afterwards; the body is kept for your delivery log and otherwise ignored."
          },
          "default": {
            "description": "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."
          }
        },
        "security": []
      }
    },
    "appointment.booked": {
      "post": {
        "tags": [
          "Webhook events"
        ],
        "summary": "An appointment was booked.",
        "description": "**Fires when** an appointment is booked by the assistant in a conversation or by the customer on the booking page.\n\n**Does not fire** when the team adds an appointment by hand in the dashboard, or when an appointment is moved to another time. Not for reminders.",
        "operationId": "webhook_appointment_booked",
        "parameters": [
          {
            "name": "X-Revoply-Signature",
            "in": "header",
            "description": "`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.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "t=1790847005,v1=3f1c9a…"
          },
          {
            "name": "X-Revoply-Event",
            "in": "header",
            "description": "The event's `type`, so a receiver can route before parsing.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "appointment.booked"
          },
          {
            "name": "X-Revoply-Delivery",
            "in": "header",
            "description": "This delivery's id. A resend of the same event keeps the envelope `id` and gets a new delivery id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "5a9e2c7b-1f34-4d86-b0a5-3c8e7d2f1b69"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "appointment.booked"
                      },
                      "data": {
                        "$ref": "#/components/schemas/AppointmentData"
                      }
                    }
                  }
                ]
              },
              "example": {
                "id": "evt_dd925c487485c878d83075c6cb317e1c",
                "type": "appointment.booked",
                "apiVersion": "2026-10",
                "createdAt": "2026-10-01T09:30:05+00:00",
                "test": false,
                "company": {
                  "id": "0b3d6f2e-8a41-4c75-9e20-5f7a1c3d9b84",
                  "name": "متجر الرياض"
                },
                "data": {
                  "appointment": {
                    "id": "c84f1a26-3e5b-4d97-8b02-7a6e9d1c5f34",
                    "reference": "BK-7Q4M2",
                    "service": "قص شعر وتصفيف",
                    "startsAt": "2026-10-03T16:30:00+00:00",
                    "endsAt": "2026-10-03T17:30:00+00:00",
                    "status": "confirmed",
                    "customerName": "نورة العتيبي",
                    "cancellationReason": null
                  },
                  "conversation": {
                    "id": "WhatsApp_3f6a2d18-0b7c-4e91-8a5d-c24e1f9b7a60",
                    "channelId": null,
                    "channelType": null
                  },
                  "contact": {
                    "id": "3f6a2d18-0b7c-4e91-8a5d-c24e1f9b7a60",
                    "name": "نورة العتيبي",
                    "phone": "+966501234567",
                    "whatsAppId": null
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "410": {
            "description": "The endpoint is gone for good: it is switched off at once, and the account's owner is told."
          },
          "2XX": {
            "description": "Received. Answer within 10 seconds and do the work afterwards; the body is kept for your delivery log and otherwise ignored."
          },
          "default": {
            "description": "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."
          }
        },
        "security": []
      }
    },
    "appointment.cancelled": {
      "post": {
        "tags": [
          "Webhook events"
        ],
        "summary": "An appointment was cancelled.",
        "description": "**Fires when** an appointment is cancelled by the customer, the assistant or the team. `cancellationReason` is what was given, if anything.\n\n**Does not fire** when an appointment is moved, or merely passes without the customer turning up.",
        "operationId": "webhook_appointment_cancelled",
        "parameters": [
          {
            "name": "X-Revoply-Signature",
            "in": "header",
            "description": "`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.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "t=1790847005,v1=3f1c9a…"
          },
          {
            "name": "X-Revoply-Event",
            "in": "header",
            "description": "The event's `type`, so a receiver can route before parsing.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "appointment.cancelled"
          },
          {
            "name": "X-Revoply-Delivery",
            "in": "header",
            "description": "This delivery's id. A resend of the same event keeps the envelope `id` and gets a new delivery id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "5a9e2c7b-1f34-4d86-b0a5-3c8e7d2f1b69"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "appointment.cancelled"
                      },
                      "data": {
                        "$ref": "#/components/schemas/AppointmentData"
                      }
                    }
                  }
                ]
              },
              "example": {
                "id": "evt_4f7bfba09c0ddbe25c94ad50aadeb346",
                "type": "appointment.cancelled",
                "apiVersion": "2026-10",
                "createdAt": "2026-10-01T09:30:05+00:00",
                "test": false,
                "company": {
                  "id": "0b3d6f2e-8a41-4c75-9e20-5f7a1c3d9b84",
                  "name": "متجر الرياض"
                },
                "data": {
                  "appointment": {
                    "id": "c84f1a26-3e5b-4d97-8b02-7a6e9d1c5f34",
                    "reference": "BK-7Q4M2",
                    "service": "قص شعر وتصفيف",
                    "startsAt": "2026-10-03T16:30:00+00:00",
                    "endsAt": "2026-10-03T17:30:00+00:00",
                    "status": "cancelled",
                    "customerName": "نورة العتيبي",
                    "cancellationReason": "ظرف طارئ، سأحجز موعدًا آخر"
                  },
                  "conversation": {
                    "id": "WhatsApp_3f6a2d18-0b7c-4e91-8a5d-c24e1f9b7a60",
                    "channelId": null,
                    "channelType": null
                  },
                  "contact": {
                    "id": "3f6a2d18-0b7c-4e91-8a5d-c24e1f9b7a60",
                    "name": "نورة العتيبي",
                    "phone": "+966501234567",
                    "whatsAppId": null
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "410": {
            "description": "The endpoint is gone for good: it is switched off at once, and the account's owner is told."
          },
          "2XX": {
            "description": "Received. Answer within 10 seconds and do the work afterwards; the body is kept for your delivery log and otherwise ignored."
          },
          "default": {
            "description": "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."
          }
        },
        "security": []
      }
    },
    "flow.completed": {
      "post": {
        "tags": [
          "Webhook events"
        ],
        "summary": "An automation finished.",
        "description": "**Fires when** a flow run ends as completed: it reached its end, handed over to the team, the customer typed an exit word, or the assistant took over an answer the flow could not use. `run.endReason` says which; `run.variables` holds what it collected.\n\n**Does not fire** for runs that were cancelled, skipped or timed out — those are not announced.",
        "operationId": "webhook_flow_completed",
        "parameters": [
          {
            "name": "X-Revoply-Signature",
            "in": "header",
            "description": "`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.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "t=1790847005,v1=3f1c9a…"
          },
          {
            "name": "X-Revoply-Event",
            "in": "header",
            "description": "The event's `type`, so a receiver can route before parsing.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "flow.completed"
          },
          {
            "name": "X-Revoply-Delivery",
            "in": "header",
            "description": "This delivery's id. A resend of the same event keeps the envelope `id` and gets a new delivery id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "5a9e2c7b-1f34-4d86-b0a5-3c8e7d2f1b69"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "flow.completed"
                      },
                      "data": {
                        "$ref": "#/components/schemas/FlowRunData"
                      }
                    }
                  }
                ]
              },
              "example": {
                "id": "evt_fb3ec83d6924f1f79d8ed6876cfcde23",
                "type": "flow.completed",
                "apiVersion": "2026-10",
                "createdAt": "2026-10-01T09:30:05+00:00",
                "test": false,
                "company": {
                  "id": "0b3d6f2e-8a41-4c75-9e20-5f7a1c3d9b84",
                  "name": "متجر الرياض"
                },
                "data": {
                  "flow": {
                    "id": "e2a7c9d4-1b6f-4a38-9c50-8d3e7f2b1a96",
                    "name": "تأكيد الطلب"
                  },
                  "run": {
                    "id": "b16d4f8a-2c9e-4b73-a5d1-0e8f3c7a9b25",
                    "status": "completed",
                    "endReason": "completed",
                    "triggerKind": "webhook",
                    "startedAt": "2026-10-01T09:30:00+00:00",
                    "endedAt": "2026-10-01T09:33:00+00:00",
                    "variables": {
                      "order_id": "10482",
                      "confirmed": "نعم"
                    }
                  },
                  "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"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "410": {
            "description": "The endpoint is gone for good: it is switched off at once, and the account's owner is told."
          },
          "2XX": {
            "description": "Received. Answer within 10 seconds and do the work afterwards; the body is kept for your delivery log and otherwise ignored."
          },
          "default": {
            "description": "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."
          }
        },
        "security": []
      }
    },
    "flow.failed": {
      "post": {
        "tags": [
          "Webhook events"
        ],
        "summary": "An automation failed.",
        "description": "**Fires when** a flow run ends as failed: a step errored, a limit on steps or messages was reached, or the run was found stuck and ended.\n\n**Does not fire** for runs that were cancelled, skipped or timed out.",
        "operationId": "webhook_flow_failed",
        "parameters": [
          {
            "name": "X-Revoply-Signature",
            "in": "header",
            "description": "`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.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "t=1790847005,v1=3f1c9a…"
          },
          {
            "name": "X-Revoply-Event",
            "in": "header",
            "description": "The event's `type`, so a receiver can route before parsing.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "flow.failed"
          },
          {
            "name": "X-Revoply-Delivery",
            "in": "header",
            "description": "This delivery's id. A resend of the same event keeps the envelope `id` and gets a new delivery id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "5a9e2c7b-1f34-4d86-b0a5-3c8e7d2f1b69"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "flow.failed"
                      },
                      "data": {
                        "$ref": "#/components/schemas/FlowRunData"
                      }
                    }
                  }
                ]
              },
              "example": {
                "id": "evt_d069f8d7af8fcc22e28694f98657b599",
                "type": "flow.failed",
                "apiVersion": "2026-10",
                "createdAt": "2026-10-01T09:30:05+00:00",
                "test": false,
                "company": {
                  "id": "0b3d6f2e-8a41-4c75-9e20-5f7a1c3d9b84",
                  "name": "متجر الرياض"
                },
                "data": {
                  "flow": {
                    "id": "e2a7c9d4-1b6f-4a38-9c50-8d3e7f2b1a96",
                    "name": "تأكيد الطلب"
                  },
                  "run": {
                    "id": "b16d4f8a-2c9e-4b73-a5d1-0e8f3c7a9b25",
                    "status": "failed",
                    "endReason": "error",
                    "triggerKind": "webhook",
                    "startedAt": "2026-10-01T09:30:00+00:00",
                    "endedAt": "2026-10-01T09:33:00+00:00",
                    "variables": {
                      "order_id": "10482",
                      "confirmed": "نعم"
                    }
                  },
                  "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"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "410": {
            "description": "The endpoint is gone for good: it is switched off at once, and the account's owner is told."
          },
          "2XX": {
            "description": "Received. Answer within 10 seconds and do the work afterwards; the body is kept for your delivery log and otherwise ignored."
          },
          "default": {
            "description": "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."
          }
        },
        "security": []
      }
    },
    "ping": {
      "post": {
        "tags": [
          "Webhook events"
        ],
        "summary": "A test from the dashboard.",
        "description": "**Fires when** someone presses \"Send test\" on the endpoint without choosing an event. Carries `\"test\": true`.\n\n**Does not fire** on its own: it cannot be subscribed to, is sent only when someone asks for it, and is never retried.",
        "operationId": "webhook_ping",
        "parameters": [
          {
            "name": "X-Revoply-Signature",
            "in": "header",
            "description": "`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.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "t=1790847005,v1=3f1c9a…"
          },
          {
            "name": "X-Revoply-Event",
            "in": "header",
            "description": "The event's `type`, so a receiver can route before parsing.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "ping"
          },
          {
            "name": "X-Revoply-Delivery",
            "in": "header",
            "description": "This delivery's id. A resend of the same event keeps the envelope `id` and gets a new delivery id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "5a9e2c7b-1f34-4d86-b0a5-3c8e7d2f1b69"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "ping"
                      },
                      "data": {
                        "$ref": "#/components/schemas/PingData"
                      }
                    }
                  }
                ]
              },
              "example": {
                "id": "evt_2f49fe013bfeb76d088b2b7018ef6f6b",
                "type": "ping",
                "apiVersion": "2026-10",
                "createdAt": "2026-10-01T09:30:05+00:00",
                "test": true,
                "company": {
                  "id": "0b3d6f2e-8a41-4c75-9e20-5f7a1c3d9b84",
                  "name": "متجر الرياض"
                },
                "data": {
                  "message": "This is a test event from RevoplyAI."
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "410": {
            "description": "The endpoint is gone for good: it is switched off at once, and the account's owner is told."
          },
          "2XX": {
            "description": "Received. Answer within 10 seconds and do the work afterwards; the body is kept for your delivery log and otherwise ignored."
          },
          "default": {
            "description": "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."
          }
        },
        "security": []
      }
    }
  }
}
