{
  "openapi": "3.0.3",
  "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": "string",
                "nullable": true
              },
              "service": {
                "type": "string",
                "nullable": true
              },
              "startsAt": {
                "type": "string",
                "format": "date-time"
              },
              "endsAt": {
                "type": "string",
                "format": "date-time"
              },
              "status": {
                "type": "string",
                "description": "`confirmed`, `cancelled`, `no_show` or `completed`."
              },
              "customerName": {
                "type": "string",
                "nullable": true
              },
              "cancellationReason": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "conversation": {
            "required": [
              "id",
              "channelId",
              "channelType"
            ],
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The conversation's id. Opaque; may contain `_`, `.`, `+`, `=` and `@`."
              },
              "channelId": {
                "type": "string",
                "description": "The channel (number, page, bot or widget) it is on.",
                "nullable": true
              },
              "channelType": {
                "type": "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"
                ],
                "nullable": true
              }
            },
            "nullable": true
          },
          "contact": {
            "required": [
              "id",
              "name",
              "phone",
              "whatsAppId"
            ],
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The contact's id; null for a web widget visitor.",
                "nullable": true
              },
              "name": {
                "type": "string",
                "nullable": true
              },
              "phone": {
                "type": "string",
                "description": "E.164, such as `+966501234567`, or null — never a WhatsApp user id.",
                "nullable": true
              },
              "whatsAppId": {
                "type": "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.",
                "nullable": true
              }
            },
            "nullable": true
          }
        }
      },
      "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": "string",
                "nullable": true
              },
              "phone": {
                "type": "string",
                "description": "E.164, such as `+966501234567`, or null — never a WhatsApp user id.",
                "nullable": true
              },
              "whatsAppId": {
                "type": "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.",
                "nullable": true
              },
              "email": {
                "type": "string",
                "nullable": true
              },
              "language": {
                "type": "string",
                "description": "`ar`, `en`… The language the customer writes in, when known.",
                "nullable": true
              },
              "tags": {
                "type": "array",
                "items": {
                  "type": "string",
                  "nullable": true
                }
              },
              "fields": {
                "type": "object",
                "additionalProperties": {
                  "type": "string",
                  "nullable": true
                },
                "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": "string",
                "description": "The channel (number, page, bot or widget) it is on.",
                "nullable": true
              },
              "channelType": {
                "type": "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"
                ],
                "nullable": true
              }
            }
          },
          "assignee": {
            "required": [
              "id",
              "name",
              "email"
            ],
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "nullable": true
              },
              "name": {
                "type": "string",
                "nullable": true
              },
              "email": {
                "type": "string",
                "nullable": true
              }
            },
            "nullable": true
          }
        }
      },
      "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": "string",
                "description": "The channel (number, page, bot or widget) it is on.",
                "nullable": true
              },
              "channelType": {
                "type": "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"
                ],
                "nullable": true
              }
            }
          },
          "contact": {
            "required": [
              "id",
              "name",
              "phone",
              "whatsAppId"
            ],
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The contact's id; null for a web widget visitor.",
                "nullable": true
              },
              "name": {
                "type": "string",
                "nullable": true
              },
              "phone": {
                "type": "string",
                "description": "E.164, such as `+966501234567`, or null — never a WhatsApp user id.",
                "nullable": true
              },
              "whatsAppId": {
                "type": "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.",
                "nullable": true
              }
            },
            "nullable": true
          }
        }
      },
      "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": "string",
                "description": "The channel (number, page, bot or widget) it is on.",
                "nullable": true
              },
              "channelType": {
                "type": "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"
                ],
                "nullable": true
              }
            }
          },
          "contact": {
            "required": [
              "id",
              "name",
              "phone",
              "whatsAppId"
            ],
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The contact's id; null for a web widget visitor.",
                "nullable": true
              },
              "name": {
                "type": "string",
                "nullable": true
              },
              "phone": {
                "type": "string",
                "description": "E.164, such as `+966501234567`, or null — never a WhatsApp user id.",
                "nullable": true
              },
              "whatsAppId": {
                "type": "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.",
                "nullable": true
              }
            },
            "nullable": true
          },
          "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": "string",
            "description": "The assistant's own words, when it gave any. For people; may be Arabic.",
            "nullable": true
          }
        }
      },
      "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": "string",
                "description": "Why it ended: `completed`, `handed_over`, `customer_exited`, `error`, `limit_exceeded`… Treat an unknown value as its status.",
                "nullable": true
              },
              "triggerKind": {
                "type": "string",
                "description": "What started it: `webhook`, `keyword`, `conversation_started`, `manual`… May grow."
              },
              "startedAt": {
                "type": "string",
                "format": "date-time"
              },
              "endedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "variables": {
                "type": "object",
                "additionalProperties": {
                  "type": "string",
                  "nullable": true
                },
                "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": "string",
                "description": "The channel (number, page, bot or widget) it is on.",
                "nullable": true
              },
              "channelType": {
                "type": "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"
                ],
                "nullable": true
              }
            }
          },
          "contact": {
            "required": [
              "id",
              "name",
              "phone",
              "whatsAppId"
            ],
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The contact's id; null for a web widget visitor.",
                "nullable": true
              },
              "name": {
                "type": "string",
                "nullable": true
              },
              "phone": {
                "type": "string",
                "description": "E.164, such as `+966501234567`, or null — never a WhatsApp user id.",
                "nullable": true
              },
              "whatsAppId": {
                "type": "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.",
                "nullable": true
              }
            },
            "nullable": true
          }
        }
      },
      "HookResponse": {
        "required": [
          "id",
          "status",
          "detail"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "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",
            "nullable": true
          },
          "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": "string",
            "description": "Why, in words for people. May change; branch on status.",
            "nullable": true
          }
        },
        "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": "string",
                "nullable": true
              },
              "status": {
                "type": "string",
                "description": "`qualified` on this event."
              },
              "temperature": {
                "type": "string",
                "description": "`cold`, `warm` or `hot`.",
                "nullable": true
              },
              "score": {
                "type": "integer",
                "description": "0 to 100.",
                "nullable": true
              },
              "qualifiedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "conversation": {
            "required": [
              "id",
              "channelId",
              "channelType"
            ],
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The conversation's id. Opaque; may contain `_`, `.`, `+`, `=` and `@`."
              },
              "channelId": {
                "type": "string",
                "description": "The channel (number, page, bot or widget) it is on.",
                "nullable": true
              },
              "channelType": {
                "type": "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"
                ],
                "nullable": true
              }
            },
            "nullable": true
          }
        }
      },
      "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": "string",
                "description": "The channel (number, page, bot or widget) it is on.",
                "nullable": true
              },
              "channelType": {
                "type": "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"
                ],
                "nullable": true
              }
            }
          },
          "contact": {
            "required": [
              "id",
              "name",
              "phone",
              "whatsAppId"
            ],
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The contact's id; null for a web widget visitor.",
                "nullable": true
              },
              "name": {
                "type": "string",
                "nullable": true
              },
              "phone": {
                "type": "string",
                "description": "E.164, such as `+966501234567`, or null — never a WhatsApp user id.",
                "nullable": true
              },
              "whatsAppId": {
                "type": "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.",
                "nullable": true
              }
            },
            "nullable": true
          },
          "message": {
            "required": [
              "id",
              "text",
              "type",
              "receivedAt"
            ],
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "nullable": true
              },
              "text": {
                "type": "string",
                "description": "The text, or a media message's caption; cut at 4,096 characters.",
                "nullable": true
              },
              "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": "string",
            "example": "Send the number with its country code, such as +9665XXXXXXXX.",
            "nullable": true
          },
          "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": "array",
            "items": {
              "$ref": "#/components/schemas/ProblemError"
            },
            "description": "What to fix, field by field, when there is more than one thing.",
            "nullable": true
          }
        },
        "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": "string",
                "nullable": true
              }
            }
          },
          "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"
      ]
    }
  ]
}
