{
  "openapi": "3.1.0",
  "info": {
    "title": "Wylto API",
    "version": "v1.0",
    "description": "Send WhatsApp messages and templates, manage contacts and run campaigns from your own systems, and get webhooks when messages arrive or bookings change.\n\n## Getting started\n\n1. In Wylto, open **CRM → Settings → API Settings** and copy your API token. Only the app owner can see it.\n2. Send it as a Bearer token: `Authorization: Bearer <token>`.\n3. Call the API at `https://server.wylto.com`. Requests and responses are JSON.\n\n## Webhooks\n\n- **Messages:** Wylto posts each new WhatsApp message to your URL.\n- **Bookings:** set up under **Appointments → Settings → Webhooks** to get created, rescheduled, cancelled and completed bookings, signed with your secret.\n\n## Errors\n\nFailed requests return a 4xx or 5xx status with a JSON body that describes the problem.",
    "x-logo": {
      "url": "https://app.wylto.com/wylto-logo.avif",
      "altText": "Wylto"
    }
  },
  "servers": [
    {
      "url": "https://server.wylto.com/",
      "description": "Main production server for Wylto"
    }
  ],
  "schemes": [
    "https"
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your API token, sent as `Authorization: Bearer <token>`. Find it in Wylto under **CRM → Settings → API Settings** (app owner only)."
      }
    },
    "schemas": {
      "WhatsappMessage": {
        "type": "object",
        "properties": {
          "context": {
            "$ref": "#/components/schemas/Context"
          },
          "from": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "type": {
            "type": "enum",
            "enum": [
              "text",
              "button",
              "document",
              "location",
              "image",
              "video",
              "audio",
              "order",
              "interactive"
            ]
          },
          "text": {
            "$ref": "#/components/schemas/Text"
          },
          "button": {
            "$ref": "#/components/schemas/Button"
          },
          "document": {
            "$ref": "#/components/schemas/Document"
          },
          "location": {
            "$ref": "#/components/schemas/Location"
          },
          "image": {
            "$ref": "#/components/schemas/Media"
          },
          "video": {
            "$ref": "#/components/schemas/Media"
          },
          "audio": {
            "$ref": "#/components/schemas/Audio"
          },
          "order": {
            "$ref": "#/components/schemas/Order"
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Error"
            }
          },
          "interactive": {
            "$ref": "#/components/schemas/Interactive"
          }
        }
      },
      "Context": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string"
          },
          "id": {
            "type": "string"
          }
        }
      },
      "Text": {
        "type": "object",
        "properties": {
          "body": {
            "type": "string"
          }
        }
      },
      "Button": {
        "type": "object",
        "properties": {
          "payload": {
            "type": "string"
          },
          "text": {
            "type": "string"
          }
        }
      },
      "Document": {
        "type": "object",
        "properties": {
          "caption": {
            "type": "string"
          },
          "fileName": {
            "type": "string"
          },
          "mimeType": {
            "type": "string"
          },
          "sha256": {
            "type": "string"
          },
          "id": {
            "type": "string"
          }
        }
      },
      "Location": {
        "type": "object",
        "properties": {
          "latitude": {
            "type": "number",
            "format": "double"
          },
          "longitude": {
            "type": "number",
            "format": "double"
          },
          "name": {
            "type": "string"
          },
          "address": {
            "type": "string"
          }
        }
      },
      "Media": {
        "type": "object",
        "properties": {
          "caption": {
            "type": "string"
          },
          "mimeType": {
            "type": "string"
          },
          "sha256": {
            "type": "string"
          },
          "id": {
            "type": "string"
          }
        }
      },
      "Audio": {
        "type": "object",
        "properties": {
          "caption": {
            "type": "string"
          },
          "mimeType": {
            "type": "string"
          },
          "sha256": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "voice": {
            "type": "boolean"
          }
        }
      },
      "Order": {
        "type": "object",
        "properties": {
          "catalogId": {
            "type": "string"
          },
          "productItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductItem"
            }
          }
        }
      },
      "ProductItem": {
        "type": "object",
        "properties": {
          "productRetailerId": {
            "type": "string"
          },
          "itemPrice": {
            "type": "integer"
          },
          "currency": {
            "type": "string"
          },
          "quantity": {
            "type": "integer"
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "Interactive": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string"
          },
          "listReply": {
            "$ref": "#/components/schemas/ListReply"
          },
          "buttonReply": {
            "$ref": "#/components/schemas/ButtonReply"
          },
          "nfmReply": {
            "$ref": "#/components/schemas/NfmReply"
          }
        }
      },
      "ListReply": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          }
        }
      },
      "ButtonReply": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          }
        }
      },
      "NfmReply": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "body": {
            "type": "string"
          },
          "responseJSON": {
            "type": "string"
          }
        }
      },
      "OnNewMessagePayload": {
        "type": "object",
        "properties": {
          "contactId": {
            "type": "string",
            "description": "Unique identifier of the contact in wylto."
          },
          "clientContactId": {
            "type": "string",
            "description": "Unique identifier of the contact in your system."
          },
          "conversationId": {
            "type": "string",
            "description": "Unique identifier of the conversation in wylto."
          },
          "clientMessageId": {
            "type": "string",
            "description": "Unique identifier of the message in your system. You need to provide this as part of the request when sending a message."
          },
          "message": {
            "type": "object",
            "$ref": "#/components/schemas/WhatsappMessage"
          }
        }
      },
      "EventResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "description": "Confirmation message that the event was received."
          }
        }
      },
      "SendMessageRequest": {
        "type": "object",
        "properties": {
          "to": {
            "type": "string",
            "description": "The phone number to which the message is being sent."
          },
          "clientMessageId": {
            "type": "string",
            "description": "Unique identifier of the message in your system."
          },
          "message": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "description": "The type of the message.",
                "enum": [
                  "text",
                  "template",
                  "image",
                  "document",
                  "video",
                  "audio",
                  "buttons",
                  "list"
                ]
              },
              "text": {
                "type": "object",
                "description": "Text message content. Required when type is 'text'.",
                "properties": {
                  "message": {
                    "type": "string",
                    "description": "The text content of the message."
                  }
                },
                "required": [
                  "message"
                ]
              },
              "image": {
                "type": "object",
                "description": "Image message content. Required when type is 'image'.",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "The URL of the image."
                  },
                  "mimeType": {
                    "type": "string",
                    "description": "The MIME type of the image (e.g., image/jpeg, image/png)."
                  },
                  "message": {
                    "type": "string",
                    "description": "Optional caption for the image."
                  }
                },
                "required": [
                  "url",
                  "mimeType"
                ]
              },
              "document": {
                "type": "object",
                "description": "Document message content. Required when type is 'document'.",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "The URL of the document."
                  },
                  "filename": {
                    "type": "string",
                    "description": "The filename of the document."
                  },
                  "mimeType": {
                    "type": "string",
                    "description": "The MIME type of the document."
                  },
                  "message": {
                    "type": "string",
                    "description": "Optional caption for the document."
                  }
                },
                "required": [
                  "url",
                  "mimeType"
                ]
              },
              "video": {
                "type": "object",
                "description": "Video message content. Required when type is 'video'.",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "The URL of the video."
                  },
                  "mimeType": {
                    "type": "string",
                    "description": "The MIME type of the video."
                  },
                  "message": {
                    "type": "string",
                    "description": "Optional caption for the video."
                  }
                },
                "required": [
                  "url",
                  "mimeType"
                ]
              },
              "audio": {
                "type": "object",
                "description": "Audio message content. Required when type is 'audio'.",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "The URL of the audio file."
                  },
                  "mimeType": {
                    "type": "string",
                    "description": "The MIME type of the audio."
                  }
                },
                "required": [
                  "url",
                  "mimeType"
                ]
              },
              "buttons": {
                "type": "object",
                "description": "Quick reply buttons message content. Required when type is 'buttons'. WhatsApp allows up to 3 quick reply buttons.",
                "properties": {
                  "imageURL": {
                    "type": "string",
                    "description": "Optional image URL shown as a header above the message."
                  },
                  "heading": {
                    "type": "string",
                    "description": "Optional text header shown above the message (ignored when imageURL is set)."
                  },
                  "message": {
                    "type": "string",
                    "description": "The body text shown above the buttons."
                  },
                  "footer": {
                    "type": "string",
                    "description": "Optional footer text shown below the buttons."
                  },
                  "buttons": {
                    "type": "array",
                    "description": "Up to 3 quick reply buttons.",
                    "maxItems": 3,
                    "items": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "description": "Stable identifier returned in the user's reply payload."
                        },
                        "name": {
                          "type": "string",
                          "description": "Label shown on the button (max 20 characters)."
                        }
                      },
                      "required": [
                        "id",
                        "name"
                      ]
                    }
                  }
                },
                "required": [
                  "message",
                  "buttons"
                ]
              },
              "list": {
                "type": "object",
                "description": "List message content. Required when type is 'list'. WhatsApp allows up to 10 list rows.",
                "properties": {
                  "imageURL": {
                    "type": "string",
                    "description": "Optional image URL shown as a header above the message."
                  },
                  "heading": {
                    "type": "string",
                    "description": "Optional text header shown above the message (ignored when imageURL is set)."
                  },
                  "message": {
                    "type": "string",
                    "description": "The body text shown above the list."
                  },
                  "footer": {
                    "type": "string",
                    "description": "Optional footer text shown below the list."
                  },
                  "buttonTitle": {
                    "type": "string",
                    "description": "Label of the button that opens the list (max 20 characters)."
                  },
                  "listItems": {
                    "type": "array",
                    "description": "Up to 10 selectable rows.",
                    "maxItems": 10,
                    "items": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "description": "Stable identifier returned in the user's selection payload."
                        },
                        "name": {
                          "type": "string",
                          "description": "Row title shown to the user (max 24 characters)."
                        },
                        "description": {
                          "type": "string",
                          "description": "Optional secondary line shown under the title (max 72 characters)."
                        }
                      },
                      "required": [
                        "id",
                        "name"
                      ]
                    }
                  }
                },
                "required": [
                  "message",
                  "buttonTitle",
                  "listItems"
                ]
              },
              "template": {
                "type": "object",
                "description": "Template message content. Required when type is 'template'.",
                "properties": {
                  "templateName": {
                    "type": "string",
                    "description": "The name of the template."
                  },
                  "language": {
                    "type": "string",
                    "description": "The language of the template. Refer https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates/supported-languages/ for the codes"
                  },
                  "header": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "text"
                        ],
                        "description": "The type of the header."
                      },
                      "text": {
                        "type": "string",
                        "description": "The text content of the header."
                      }
                    }
                  },
                  "body": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "type": {
                          "type": "string",
                          "enum": [
                            "text"
                          ],
                          "description": "The type of the body item."
                        },
                        "text": {
                          "type": "string",
                          "description": "The text content of the body item."
                        }
                      }
                    }
                  },
                  "buttons": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "type": {
                          "type": "string",
                          "description": "The type of the button."
                        },
                        "payload": {
                          "type": "string",
                          "description": "The payload of the button."
                        },
                        "index": {
                          "type": "integer",
                          "description": "The index of the button."
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "SendMessageToContactRequest": {
        "type": "object",
        "properties": {
          "message": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "description": "The type of the message.",
                "enum": [
                  "text",
                  "template",
                  "image",
                  "document",
                  "video",
                  "audio",
                  "buttons",
                  "list"
                ]
              },
              "text": {
                "type": "object",
                "description": "Text message content. Required when type is 'text'.",
                "properties": {
                  "message": {
                    "type": "string",
                    "description": "The text content of the message."
                  }
                },
                "required": [
                  "message"
                ]
              },
              "image": {
                "type": "object",
                "description": "Image message content. Required when type is 'image'.",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "The URL of the image."
                  },
                  "mimeType": {
                    "type": "string",
                    "description": "The MIME type of the image (e.g., image/jpeg)."
                  },
                  "message": {
                    "type": "string",
                    "description": "Optional caption for the image."
                  }
                },
                "required": [
                  "url",
                  "mimeType"
                ]
              },
              "document": {
                "type": "object",
                "description": "Document message content. Required when type is 'document'.",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "The URL of the document."
                  },
                  "filename": {
                    "type": "string",
                    "description": "The filename of the document."
                  },
                  "mimeType": {
                    "type": "string",
                    "description": "The MIME type of the document."
                  },
                  "message": {
                    "type": "string",
                    "description": "Optional caption for the document."
                  }
                },
                "required": [
                  "url",
                  "mimeType"
                ]
              },
              "video": {
                "type": "object",
                "description": "Video message content. Required when type is 'video'.",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "The URL of the video."
                  },
                  "mimeType": {
                    "type": "string",
                    "description": "The MIME type of the video."
                  },
                  "message": {
                    "type": "string",
                    "description": "Optional caption for the video."
                  }
                },
                "required": [
                  "url",
                  "mimeType"
                ]
              },
              "audio": {
                "type": "object",
                "description": "Audio message content. Required when type is 'audio'.",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "The URL of the audio file."
                  },
                  "mimeType": {
                    "type": "string",
                    "description": "The MIME type of the audio."
                  }
                },
                "required": [
                  "url",
                  "mimeType"
                ]
              },
              "buttons": {
                "type": "object",
                "description": "Quick reply buttons message content. Required when type is 'buttons'. WhatsApp allows up to 3 quick reply buttons.",
                "properties": {
                  "imageURL": {
                    "type": "string",
                    "description": "Optional image URL shown as a header above the message."
                  },
                  "heading": {
                    "type": "string",
                    "description": "Optional text header shown above the message (ignored when imageURL is set)."
                  },
                  "message": {
                    "type": "string",
                    "description": "The body text shown above the buttons."
                  },
                  "footer": {
                    "type": "string",
                    "description": "Optional footer text shown below the buttons."
                  },
                  "buttons": {
                    "type": "array",
                    "description": "Up to 3 quick reply buttons.",
                    "maxItems": 3,
                    "items": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "description": "Stable identifier returned in the user's reply payload."
                        },
                        "name": {
                          "type": "string",
                          "description": "Label shown on the button (max 20 characters)."
                        }
                      },
                      "required": [
                        "id",
                        "name"
                      ]
                    }
                  }
                },
                "required": [
                  "message",
                  "buttons"
                ]
              },
              "list": {
                "type": "object",
                "description": "List message content. Required when type is 'list'. WhatsApp allows up to 10 list rows.",
                "properties": {
                  "imageURL": {
                    "type": "string",
                    "description": "Optional image URL shown as a header above the message."
                  },
                  "heading": {
                    "type": "string",
                    "description": "Optional text header shown above the message (ignored when imageURL is set)."
                  },
                  "message": {
                    "type": "string",
                    "description": "The body text shown above the list."
                  },
                  "footer": {
                    "type": "string",
                    "description": "Optional footer text shown below the list."
                  },
                  "buttonTitle": {
                    "type": "string",
                    "description": "Label of the button that opens the list (max 20 characters)."
                  },
                  "listItems": {
                    "type": "array",
                    "description": "Up to 10 selectable rows.",
                    "maxItems": 10,
                    "items": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "description": "Stable identifier returned in the user's selection payload."
                        },
                        "name": {
                          "type": "string",
                          "description": "Row title shown to the user (max 24 characters)."
                        },
                        "description": {
                          "type": "string",
                          "description": "Optional secondary line shown under the title (max 72 characters)."
                        }
                      },
                      "required": [
                        "id",
                        "name"
                      ]
                    }
                  }
                },
                "required": [
                  "message",
                  "buttonTitle",
                  "listItems"
                ]
              },
              "template": {
                "type": "object",
                "properties": {
                  "templateName": {
                    "type": "string",
                    "description": "The name of the template."
                  },
                  "header": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "text"
                        ],
                        "description": "The type of the header."
                      },
                      "text": {
                        "type": "string",
                        "description": "The text content of the header."
                      }
                    }
                  },
                  "body": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "type": {
                          "type": "string",
                          "enum": [
                            "text"
                          ],
                          "description": "The type of the body item."
                        },
                        "text": {
                          "type": "string",
                          "description": "The text content of the body item."
                        }
                      }
                    }
                  },
                  "buttons": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "type": {
                          "type": "string",
                          "description": "The type of the button."
                        },
                        "payload": {
                          "type": "string",
                          "description": "The payload of the button."
                        },
                        "index": {
                          "type": "integer",
                          "description": "The index of the button."
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "ContactRequest": {
        "type": "object",
        "properties": {
          "externalId": {
            "type": "string",
            "description": "The unique identifier of the contact in your system."
          },
          "name": {
            "type": "string",
            "description": "The name of the contact."
          },
          "phoneNumber": {
            "type": "string",
            "description": "The phone number of the contact in E.164 format (with leading '+'). For the update-by-phone endpoint, '+' is auto-prepended if missing and the value must contain at least 10 digits."
          },
          "email": {
            "type": "string",
            "description": "The email address of the contact."
          },
          "message": {
            "type": "string",
            "description": "Free-text note attached to the contact."
          },
          "status": {
            "type": "string",
            "description": "Status ID. Takes precedence over 'statusName' when both are provided."
          },
          "statusName": {
            "type": "string",
            "description": "Status name. Resolved to the status ID via a case-insensitive lookup against the app's configured statuses. Ignored when 'status' is provided. Returns 400 if the name does not exist."
          },
          "group": {
            "type": "string",
            "description": "Group ID. Takes precedence over 'groupName' when both are provided."
          },
          "groupName": {
            "type": "string",
            "description": "Group name. Resolved to the group ID via a case-insensitive lookup against the app's configured groups. Ignored when 'group' is provided. Returns 400 if the name does not exist."
          },
          "source": {
            "type": "string",
            "description": "Acquisition source for the contact (e.g. 'web', 'whatsapp', 'partner')."
          },
          "eventAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix timestamp in milliseconds tied to the contact's status (used when the status represents a scheduled event)."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "List of tag IDs. Replaces the contact's tags. Takes precedence over 'tagNames' when both are provided."
          },
          "tagNames": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "List of tag names. Each is resolved to a tag ID via a case-insensitive lookup against the app's configured tags. Ignored when 'tags' is provided. Returns 400 if any name does not exist."
          },
          "utm": {
            "$ref": "#/components/schemas/UTMTags"
          }
        }
      },
      "ContactResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The unique identifier of the contact in Wylto."
          },
          "externalId": {
            "type": "string",
            "description": "The unique identifier of the contact in your system."
          },
          "name": {
            "type": "string",
            "description": "The name of the contact."
          },
          "phoneNumber": {
            "type": "string",
            "description": "The phone number of the contact in E.164 format."
          },
          "email": {
            "type": "string",
            "description": "The email address of the contact."
          },
          "message": {
            "type": "string",
            "description": "Free-text note attached to the contact."
          },
          "status": {
            "type": "string",
            "description": "Status ID currently set on the contact."
          },
          "group": {
            "type": "string",
            "description": "Group ID currently set on the contact."
          },
          "source": {
            "type": "string",
            "description": "Acquisition source for the contact."
          },
          "eventAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix timestamp in milliseconds tied to the contact's status."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tag IDs currently set on the contact."
          },
          "createdAt": {
            "type": "integer",
            "description": "Unix timestamp in milli seconds of when the contact was created."
          },
          "updatedAt": {
            "type": "integer",
            "description": "Unix timestamp in milli seconds of when the contact was last updated."
          },
          "utm": {
            "$ref": "#/components/schemas/UTMTags"
          }
        }
      },
      "ActivityResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The unique identifier of the activity."
          },
          "contactId": {
            "type": "string",
            "description": "The contact this activity belongs to."
          },
          "type": {
            "type": "string",
            "description": "Activity type.",
            "enum": [
              "STATUS_UPDATE",
              "ASSIGNED_TO_UPDATE",
              "GROUP_UPDATE",
              "CALL_LOG",
              "NOTE",
              "CHAT_CREATED",
              "CHAT_OPENED",
              "CHAT_RESOLVED",
              "CHAT_RESPONDED",
              "LIVE_CHAT_REQUESTED",
              "CONTACT_CREATED",
              "METRIC",
              "BROADCAST",
              "WORKFLOW",
              "EVENT"
            ]
          },
          "from": {
            "type": "string",
            "description": "Previous value, for update activities (status, group, assignee)."
          },
          "to": {
            "type": "string",
            "description": "New value, for update activities (status, group, assignee)."
          },
          "note": {
            "type": "string",
            "description": "Note text, for NOTE activities."
          },
          "author": {
            "type": "string",
            "description": "Who performed the action."
          },
          "memberId": {
            "type": "string",
            "description": "Team member linked to the activity."
          },
          "channel": {
            "type": "string",
            "description": "Channel the activity happened on, e.g. whatsapp."
          },
          "conversationId": {
            "type": "string",
            "description": "Related conversation, for chat activities."
          },
          "campaignId": {
            "type": "string",
            "description": "Related broadcast or workflow."
          },
          "duration": {
            "type": "integer",
            "description": "Call duration as sent to the call log API, for CALL_LOG activities."
          },
          "createdAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix timestamp in milliseconds."
          }
        }
      },
      "UTMTags": {
        "type": "object",
        "description": "Where the lead came from (first touch). Saved when the contact is created. On update it is saved only if the contact has no UTM yet; it is never overwritten.",
        "properties": {
          "source": {
            "type": "string",
            "description": "utm_source, e.g. 'google', 'facebook'."
          },
          "medium": {
            "type": "string",
            "description": "utm_medium, e.g. 'cpc', 'email'."
          },
          "campaign": {
            "type": "string",
            "description": "utm_campaign."
          },
          "term": {
            "type": "string",
            "description": "utm_term."
          },
          "content": {
            "type": "string",
            "description": "utm_content."
          },
          "adGroup": {
            "type": "string",
            "description": "Ad group name or ID."
          },
          "ad": {
            "type": "string",
            "description": "Ad name or ID."
          },
          "keyword": {
            "type": "string",
            "description": "Search keyword."
          },
          "gclid": {
            "type": "string",
            "description": "Google Ads click ID."
          },
          "fbclid": {
            "type": "string",
            "description": "Facebook click ID."
          }
        }
      },
      "CreateCampaignRequest": {
        "type": "object",
        "required": [
          "campaignName",
          "contacts",
          "message"
        ],
        "properties": {
          "campaignName": {
            "type": "string",
            "description": "The name of the campaign."
          },
          "contacts": {
            "type": "array",
            "description": "List of contacts to send the campaign to.",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "description": "The name of the contact."
                },
                "phoneNumber": {
                  "type": "string",
                  "description": "The phone number of the contact."
                }
              }
            }
          },
          "message": {
            "type": "object",
            "description": "The message to send.",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "template"
                ],
                "description": "The type of message."
              },
              "template": {
                "type": "object",
                "properties": {
                  "templateName": {
                    "type": "string",
                    "description": "The name of the template."
                  },
                  "language": {
                    "type": "string",
                    "description": "The language code of the template."
                  },
                  "header": {
                    "type": "object",
                    "description": "Header value, needed when the template has a variable or media header. For an image header use type \"image\" with a public image link.",
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "text",
                          "image"
                        ]
                      },
                      "text": {
                        "type": "string",
                        "description": "Header text. Used when type is \"text\"."
                      },
                      "image": {
                        "type": "object",
                        "description": "Used when type is \"image\".",
                        "properties": {
                          "link": {
                            "type": "string",
                            "description": "Public URL of the image (JPG or PNG)."
                          }
                        }
                      }
                    }
                  },
                  "body": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "type": {
                          "type": "string"
                        },
                        "text": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "CampaignResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The unique identifier of the campaign."
          },
          "name": {
            "type": "string",
            "description": "The name of the campaign."
          },
          "type": {
            "type": "string",
            "description": "The type of campaign (csv)."
          },
          "status": {
            "type": "string",
            "description": "The status of the campaign.",
            "enum": [
              "scheduled",
              "completed",
              "failed"
            ]
          },
          "channel": {
            "type": "string",
            "description": "The channel used (whatsapp)."
          },
          "count": {
            "type": "integer",
            "description": "Total message count."
          },
          "sent": {
            "type": "integer",
            "description": "Number of messages sent."
          },
          "delivered": {
            "type": "integer",
            "description": "Number of messages delivered."
          },
          "read": {
            "type": "integer",
            "description": "Number of messages read."
          },
          "failed": {
            "type": "integer",
            "description": "Number of messages failed."
          },
          "createdAt": {
            "type": "integer",
            "description": "Unix timestamp in milliseconds."
          }
        }
      },
      "CampaignReportResponse": {
        "type": "object",
        "properties": {
          "campaign": {
            "$ref": "#/components/schemas/CampaignResponse"
          },
          "messages": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Message ID."
                },
                "name": {
                  "type": "string",
                  "description": "Contact name."
                },
                "phoneNumber": {
                  "type": "string",
                  "description": "Contact phone number."
                },
                "state": {
                  "type": "string",
                  "description": "Message state.",
                  "enum": [
                    "accepted",
                    "sent",
                    "delivered",
                    "read",
                    "failed"
                  ]
                },
                "error": {
                  "type": "string",
                  "description": "Error message if failed."
                },
                "buttonClicks": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "List of button titles clicked."
                },
                "createdAt": {
                  "type": "integer",
                  "description": "Unix timestamp in milliseconds."
                }
              }
            }
          }
        }
      },
      "TemplateQualityResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Template ID."
          },
          "name": {
            "type": "string",
            "description": "Template name."
          },
          "status": {
            "type": "string",
            "description": "Template status.",
            "enum": [
              "APPROVED",
              "PENDING",
              "REJECTED"
            ]
          },
          "qualityScore": {
            "type": "string",
            "description": "Quality score.",
            "enum": [
              "GREEN",
              "YELLOW",
              "RED",
              "UNKNOWN"
            ]
          }
        }
      },
      "PhoneNumberDetailsResponse": {
        "type": "object",
        "properties": {
          "verified_name": {
            "type": "string",
            "description": "The verified business name."
          },
          "display_phone_number": {
            "type": "string",
            "description": "The display phone number."
          },
          "quality_rating": {
            "type": "string",
            "description": "Phone number quality rating.",
            "enum": [
              "GREEN",
              "YELLOW",
              "RED"
            ]
          },
          "messaging_limit_tier": {
            "type": "string",
            "description": "Current messaging limit tier."
          },
          "platform_type": {
            "type": "string",
            "description": "Platform type (CLOUD_API or ON_PREMISE)."
          },
          "throughput": {
            "type": "object",
            "properties": {
              "level": {
                "type": "string",
                "description": "Throughput level."
              }
            }
          },
          "code_verification_status": {
            "type": "string",
            "description": "Code verification status."
          },
          "last_onboarded_time": {
            "type": "string",
            "description": "Last onboarded time."
          },
          "id": {
            "type": "string",
            "description": "Phone number ID."
          }
        }
      },
      "CreateTemplateRequest": {
        "type": "object",
        "required": [
          "name",
          "language",
          "category",
          "components"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Template name (lowercase, underscores only)."
          },
          "language": {
            "type": "string",
            "description": "Language code (e.g., en, en_US)."
          },
          "category": {
            "type": "string",
            "description": "Template category.",
            "enum": [
              "MARKETING",
              "UTILITY",
              "AUTHENTICATION"
            ]
          },
          "components": {
            "type": "array",
            "description": "Template components.",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "enum": [
                    "HEADER",
                    "BODY",
                    "FOOTER",
                    "BUTTONS"
                  ]
                },
                "format": {
                  "type": "string",
                  "enum": [
                    "TEXT",
                    "IMAGE",
                    "VIDEO",
                    "DOCUMENT"
                  ]
                },
                "text": {
                  "type": "string"
                },
                "mediaUrl": {
                  "type": "string",
                  "description": "For media HEADERs (IMAGE/VIDEO/DOCUMENT): a public URL to the media. The server uploads it to WhatsApp and generates the media handle automatically. Alternatively, supply a pre-uploaded handle via example.header_handle."
                },
                "buttons": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "QUICK_REPLY",
                          "URL",
                          "PHONE_NUMBER"
                        ]
                      },
                      "text": {
                        "type": "string"
                      },
                      "url": {
                        "type": "string"
                      },
                      "phone_number": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "CreateTemplateResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The created template ID."
          },
          "status": {
            "type": "string",
            "description": "Template status."
          },
          "category": {
            "type": "string",
            "description": "Template category."
          }
        }
      },
      "WhatsAppTemplate": {
        "type": "object",
        "description": "WhatsApp message template returned in list response.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Template ID."
          },
          "name": {
            "type": "string",
            "description": "Template name."
          },
          "language": {
            "type": "string",
            "description": "Language code."
          },
          "category": {
            "type": "string",
            "description": "Template category.",
            "enum": [
              "MARKETING",
              "UTILITY",
              "AUTHENTICATION"
            ]
          },
          "status": {
            "type": "string",
            "description": "Template status.",
            "enum": [
              "APPROVED",
              "PENDING",
              "REJECTED"
            ]
          },
          "components": {
            "type": "array",
            "description": "Template components (header, body, footer, buttons).",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string"
                },
                "format": {
                  "type": "string"
                },
                "text": {
                  "type": "string"
                },
                "buttons": {
                  "type": "array",
                  "items": {
                    "type": "object"
                  }
                }
              }
            }
          }
        }
      },
      "WhatsAppMediaUrlResponse": {
        "type": "object",
        "description": "Media metadata returned when resolving a WhatsApp Cloud API media id via the Graph API (proxied by this endpoint).",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Short-lived URL to download the binary. Per Meta, fetch this URL with Authorization: Bearer and the same WhatsApp access token used by the Cloud API."
          },
          "mime_type": {
            "type": "string",
            "description": "MIME type of the media (for example image/jpeg, application/pdf)."
          }
        },
        "required": [
          "url",
          "mime_type"
        ]
      },
      "WhatsAppMediaDownloadRequest": {
        "type": "object",
        "description": "Download a short-lived WhatsApp media file from the url returned by GET /api/v1/wa/media/:mediaId. Only https URLs on Meta lookaside hosts (*.fbsbx.com) are accepted.",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Temporary download URL from the WhatsApp Cloud API media response (field url)."
          },
          "mime_type": {
            "type": "string",
            "description": "Optional MIME type for the response Content-Type (e.g. from the media metadata response). Defaults to application/octet-stream."
          },
          "filename": {
            "type": "string",
            "description": "Optional filename for Content-Disposition attachment. Path segments are stripped to a base name."
          }
        },
        "required": [
          "url"
        ]
      },
      "BookingWebhook": {
        "type": "object",
        "required": [
          "triggerEvent",
          "createdAt",
          "payload"
        ],
        "properties": {
          "triggerEvent": {
            "type": "string",
            "enum": [
              "BOOKING_CREATED",
              "BOOKING_RESCHEDULED",
              "BOOKING_CANCELLED",
              "BOOKING_COMPLETED",
              "PING"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the webhook was sent, UTC."
          },
          "payload": {
            "$ref": "#/components/schemas/BookingWebhookPayload"
          }
        }
      },
      "BookingWebhookPayload": {
        "type": "object",
        "required": [
          "bookingId",
          "status",
          "startTime",
          "endTime",
          "duration"
        ],
        "properties": {
          "bookingId": {
            "type": "string",
            "description": "Booking id in Wylto."
          },
          "eventTypeId": {
            "type": "string",
            "description": "Booking page id."
          },
          "eventTypeName": {
            "type": "string",
            "description": "Booking page name, e.g. Product demo."
          },
          "status": {
            "type": "string",
            "enum": [
              "scheduled",
              "rescheduled",
              "cancelled",
              "completed"
            ]
          },
          "startTime": {
            "type": "string",
            "format": "date-time",
            "description": "Start, UTC (RFC 3339)."
          },
          "endTime": {
            "type": "string",
            "format": "date-time",
            "description": "End, UTC (RFC 3339)."
          },
          "previousStartTime": {
            "type": "string",
            "format": "date-time",
            "description": "Only on BOOKING_RESCHEDULED: the start before the change."
          },
          "duration": {
            "type": "integer",
            "description": "Minutes."
          },
          "timeZone": {
            "type": "string",
            "description": "Booker's timezone, e.g. Asia/Kolkata."
          },
          "location": {
            "type": "string",
            "enum": [
              "video",
              "call",
              "inPerson"
            ],
            "description": "video (Google Meet or a video link), call (phone call) or inPerson."
          },
          "meetingLink": {
            "type": "string",
            "format": "uri"
          },
          "contact": {
            "$ref": "#/components/schemas/BookingWebhookPerson",
            "description": "The person who booked."
          },
          "assignedTo": {
            "$ref": "#/components/schemas/BookingWebhookPerson",
            "description": "Team member who takes the meeting."
          },
          "responses": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Booking form answers, keyed by field id."
          },
          "minutesOfMeeting": {
            "type": "string",
            "description": "Only on BOOKING_COMPLETED, when notes were added."
          },
          "source": {
            "type": "string",
            "description": "How it was booked, e.g. event-booking (booking page)."
          },
          "utm": {
            "$ref": "#/components/schemas/UTMTags",
            "description": "UTM tags of the page the booking was made from (the embedding page for booking.js embeds, else the booking link). Only when it had any."
          },
          "rescheduleUrl": {
            "type": "string",
            "format": "uri",
            "description": "Link the booker uses to reschedule."
          },
          "cancelUrl": {
            "type": "string",
            "format": "uri",
            "description": "Link the booker uses to cancel."
          }
        }
      },
      "BookingWebhookPerson": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Wylto id."
          },
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "phone": {
            "type": "string",
            "description": "With country code, digits only, e.g. 919800000000."
          }
        }
      }
    }
  },
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "securityDefinitions": {
    "api_key": {
      "type": "apiKey",
      "name": "api_key",
      "in": "header"
    }
  },
  "tags": [
    {
      "name": "WhatsApp",
      "description": "Send messages and templates, manage templates, download media and read your phone number details."
    },
    {
      "name": "Contact",
      "description": "Create, update and read contacts, including UTM and activities."
    },
    {
      "name": "Campaigns",
      "description": "Create WhatsApp broadcast campaigns and read their reports."
    },
    {
      "name": "Webhooks",
      "description": "Messages Wylto posts to your server when a WhatsApp message arrives."
    },
    {
      "name": "Booking webhooks",
      "description": "Messages Wylto posts to your server when a booking is created, rescheduled, cancelled or completed."
    }
  ],
  "paths": {
    "/api/v1/wa/send": {
      "post": {
        "tags": [
          "WhatsApp"
        ],
        "summary": "Send a message to a phone number",
        "operationId": "sendMessage",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendMessageRequest"
              },
              "examples": {
                "sendTextMessage": {
                  "summary": "Example of sending a text message",
                  "value": {
                    "to": "+919876012345",
                    "message": {
                      "type": "text",
                      "text": {
                        "message": "Hi, this is a test message"
                      }
                    }
                  }
                },
                "sendImageMessage": {
                  "summary": "Example of sending an image message",
                  "value": {
                    "to": "+919876012345",
                    "message": {
                      "type": "image",
                      "image": {
                        "url": "https://example.com/image.jpg",
                        "mimeType": "image/jpeg",
                        "message": "Check out this image!"
                      }
                    }
                  }
                },
                "sendDocumentMessage": {
                  "summary": "Example of sending a document message",
                  "value": {
                    "to": "+919876012345",
                    "message": {
                      "type": "document",
                      "document": {
                        "url": "https://example.com/document.pdf",
                        "filename": "invoice.pdf",
                        "mimeType": "application/pdf",
                        "message": "Here is your invoice"
                      }
                    }
                  }
                },
                "sendQuickReplyButtonsMessage": {
                  "summary": "Example of sending quick reply buttons (max 3)",
                  "value": {
                    "to": "+919876012345",
                    "message": {
                      "type": "buttons",
                      "buttons": {
                        "heading": "Order #1029",
                        "message": "Your order is ready. What would you like to do next?",
                        "footer": "Reply with a button",
                        "buttons": [
                          {
                            "id": "track_order",
                            "name": "Track order"
                          },
                          {
                            "id": "talk_to_human",
                            "name": "Talk to agent"
                          },
                          {
                            "id": "cancel_order",
                            "name": "Cancel order"
                          }
                        ]
                      }
                    }
                  }
                },
                "sendQuickReplyButtonsWithImage": {
                  "summary": "Example of sending quick reply buttons with an image header",
                  "value": {
                    "to": "+919876012345",
                    "message": {
                      "type": "buttons",
                      "buttons": {
                        "imageURL": "https://example.com/promo.jpg",
                        "message": "Flash sale ends in 1 hour! Pick one:",
                        "buttons": [
                          {
                            "id": "shop_now",
                            "name": "Shop now"
                          },
                          {
                            "id": "remind_me",
                            "name": "Remind me later"
                          }
                        ]
                      }
                    }
                  }
                },
                "sendListMessage": {
                  "summary": "Example of sending a list message (max 10 rows)",
                  "value": {
                    "to": "+919876012345",
                    "message": {
                      "type": "list",
                      "list": {
                        "heading": "Pick a service",
                        "message": "Choose a service to continue.",
                        "footer": "Powered by Wylto",
                        "buttonTitle": "View services",
                        "listItems": [
                          {
                            "id": "haircut",
                            "name": "Haircut",
                            "description": "30 min · $25"
                          },
                          {
                            "id": "shave",
                            "name": "Beard trim",
                            "description": "15 min · $15"
                          },
                          {
                            "id": "color",
                            "name": "Hair color",
                            "description": "60 min · $60"
                          },
                          {
                            "id": "package",
                            "name": "Combo deal",
                            "description": "Haircut + beard trim · $35"
                          }
                        ]
                      }
                    }
                  }
                },
                "sendTemplateMessage": {
                  "summary": "Example of sending a WhatsApp template",
                  "value": {
                    "to": "<phoneNumber>",
                    "message": {
                      "type": "template",
                      "template": {
                        "templateName": "example_template_name",
                        "language": "en",
                        "header": {
                          "type": "text",
                          "text": "Your header text here"
                        },
                        "body": [
                          {
                            "type": "text",
                            "text": "Your body text here"
                          }
                        ],
                        "buttons": [
                          {
                            "type": "your_button_type",
                            "payload": "your_button_payload",
                            "index": 0
                          }
                        ]
                      }
                    }
                  }
                },
                "sendOtpExample": {
                  "summary": "Example of sending a otp style template message",
                  "value": {
                    "to": "<phoneNumber>",
                    "message": {
                      "type": "template",
                      "template": {
                        "templateName": "<otp_template_name>",
                        "language": "en",
                        "body": [
                          {
                            "type": "text",
                            "text": "122335"
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Message sent successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Indicates if the message was sent successfully."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. The request was unacceptable, often due to missing a required parameter."
          },
          "401": {
            "description": "Unauthorized. The request did not include a correct authorization token."
          },
          "500": {
            "description": "Internal Server Error. Something went wrong on the server side."
          }
        }
      }
    },
    "/api/v1/wa/media/:mediaId": {
      "get": {
        "tags": [
          "WhatsApp"
        ],
        "summary": "Resolve WhatsApp media id to download URL (or stream file)",
        "operationId": "getWhatsAppMediaUrl",
        "description": "Uses the app's default WhatsApp connection to call the Meta Graph API for the given media id (e.g. message.image.id). By default returns JSON with a short-lived url and mime_type. With query action=download, resolves and streams the file in one request (WhatsApp token used server-side). Alternatively use POST /api/v1/wa/media/download with the returned url.",
        "parameters": [
          {
            "name": "mediaId",
            "in": "path",
            "required": true,
            "description": "WhatsApp media object id from the Cloud API (e.g. message.image.id).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "action",
            "in": "query",
            "required": false,
            "description": "If set to download, response body is the raw media bytes instead of JSON. Content-Type comes from Meta mime_type when present.",
            "schema": {
              "type": "string",
              "enum": [
                "download"
              ]
            }
          },
          {
            "name": "filename",
            "in": "query",
            "required": false,
            "description": "When action=download, optional filename for Content-Disposition attachment (sanitized to a base name).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "produces": [
          "application/json",
          "application/octet-stream"
        ],
        "responses": {
          "200": {
            "description": "Without action=download: JSON with url and mime_type. With action=download: raw file bytes (Content-Type from media metadata, or application/octet-stream).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WhatsAppMediaUrlResponse"
                },
                "example": {
                  "url": "https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=...",
                  "mime_type": "image/jpeg"
                }
              },
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. Missing or invalid API bearer token."
          },
          "500": {
            "description": "Internal Server Error. Failed to resolve media (for example invalid media id or WhatsApp connection error)."
          },
          "502": {
            "description": "Bad gateway. Returned when action=download and the download from Meta failed, or the resolved URL host was not allowed."
          }
        }
      }
    },
    "/api/v1/wa/media/download": {
      "post": {
        "tags": [
          "WhatsApp"
        ],
        "summary": "Download WhatsApp media from temporary URL",
        "operationId": "downloadWhatsAppMediaFromUrl",
        "description": "Fetches binary content from a short-lived url returned by GET /api/v1/wa/media/:mediaId, using the app's default WhatsApp connection Bearer token when calling Meta. Response body is the raw file. Only https URLs on allowed Meta hosts (lookaside.fbsbx.com and *.fbsbx.com) are permitted.",
        "consumes": [
          "application/json"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WhatsAppMediaDownloadRequest"
              },
              "example": {
                "url": "https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=...",
                "mime_type": "image/jpeg",
                "filename": "photo.jpg"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Raw file bytes. Content-Type is taken from mime_type in the request when provided, otherwise application/octet-stream.",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/jpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Bad request (missing url, invalid URL, or host not allowed)."
          },
          "401": {
            "description": "Unauthorized. Missing or invalid API bearer token."
          },
          "502": {
            "description": "Bad gateway. Meta returned a non-success status or the download failed."
          }
        }
      }
    },
    "/api/v1/wa/send/:contactId": {
      "post": {
        "tags": [
          "WhatsApp"
        ],
        "summary": "Send a message to a contact",
        "operationId": "sendMessageToContact",
        "parameters": [
          {
            "name": "contactId",
            "in": "path",
            "required": true,
            "description": "The ID of the contact to whom the message is being sent.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendMessageToContactRequest"
              },
              "examples": {
                "sendMessageExample": {
                  "summary": "Example of sending a WhatsApp template",
                  "value": {
                    "message": {
                      "type": "template",
                      "template": {
                        "templateName": "example_template_name",
                        "header": {
                          "type": "text",
                          "text": "Your header text here"
                        },
                        "body": [
                          {
                            "type": "text",
                            "text": "Your body text here"
                          }
                        ],
                        "buttons": [
                          {
                            "type": "your_button_type",
                            "payload": "your_button_payload",
                            "index": 0
                          }
                        ]
                      }
                    }
                  }
                },
                "sendOtpExample": {
                  "summary": "Example of sending a otp style template message",
                  "value": {
                    "message": {
                      "type": "template",
                      "template": {
                        "templateName": "<otp_template_name>",
                        "body": [
                          {
                            "type": "text",
                            "text": "122335"
                          }
                        ]
                      }
                    }
                  }
                },
                "sendQuickReplyButtonsMessage": {
                  "summary": "Example of sending quick reply buttons (max 3)",
                  "value": {
                    "message": {
                      "type": "buttons",
                      "buttons": {
                        "heading": "Order #1029",
                        "message": "Your order is ready. What would you like to do next?",
                        "footer": "Reply with a button",
                        "buttons": [
                          {
                            "id": "track_order",
                            "name": "Track order"
                          },
                          {
                            "id": "talk_to_human",
                            "name": "Talk to agent"
                          },
                          {
                            "id": "cancel_order",
                            "name": "Cancel order"
                          }
                        ]
                      }
                    }
                  }
                },
                "sendQuickReplyButtonsWithImage": {
                  "summary": "Example of sending quick reply buttons with an image header",
                  "value": {
                    "message": {
                      "type": "buttons",
                      "buttons": {
                        "imageURL": "https://example.com/promo.jpg",
                        "message": "Flash sale ends in 1 hour! Pick one:",
                        "buttons": [
                          {
                            "id": "shop_now",
                            "name": "Shop now"
                          },
                          {
                            "id": "remind_me",
                            "name": "Remind me later"
                          }
                        ]
                      }
                    }
                  }
                },
                "sendListMessage": {
                  "summary": "Example of sending a list message (max 10 rows)",
                  "value": {
                    "message": {
                      "type": "list",
                      "list": {
                        "heading": "Pick a service",
                        "message": "Choose a service to continue.",
                        "footer": "Powered by Wylto",
                        "buttonTitle": "View services",
                        "listItems": [
                          {
                            "id": "haircut",
                            "name": "Haircut",
                            "description": "30 min · $25"
                          },
                          {
                            "id": "shave",
                            "name": "Beard trim",
                            "description": "15 min · $15"
                          },
                          {
                            "id": "color",
                            "name": "Hair color",
                            "description": "60 min · $60"
                          },
                          {
                            "id": "package",
                            "name": "Combo deal",
                            "description": "Haircut + beard trim · $35"
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Message sent successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Indicates if the message was sent successfully."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. The request was unacceptable, often due to missing a required parameter."
          },
          "401": {
            "description": "Unauthorized. The request did not include a correct authorization token."
          },
          "500": {
            "description": "Internal Server Error. Something went wrong on the server side."
          }
        }
      }
    },
    "/api/v1/contact": {
      "get": {
        "tags": [
          "Contact"
        ],
        "summary": "Get a contact by phone number",
        "operationId": "getContactByPhoneNumber",
        "description": "Fetches a single contact by phone number. The phone number is normalized before lookup: spaces and dashes are stripped, '+' is auto-prepended if missing, and the value must contain at least 10 digits.",
        "parameters": [
          {
            "name": "phoneNumber",
            "in": "query",
            "required": true,
            "description": "Phone number to look up the contact by. '+' is auto-prepended if missing; minimum 10 digits.",
            "schema": {
              "type": "string"
            },
            "example": "14155551234"
          }
        ],
        "responses": {
          "200": {
            "description": "Contact fetched successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactResponse"
                },
                "example": {
                  "id": "contact_xyz789",
                  "externalId": "crm-1029",
                  "name": "jane doe",
                  "phoneNumber": "+14155551234",
                  "email": "jane@example.com",
                  "message": "Met at trade show",
                  "status": "status_abc123",
                  "group": "group_sales",
                  "source": "tradeshow",
                  "eventAt": 1740009600000,
                  "tags": [
                    "tag_vip",
                    "tag_newsletter"
                  ],
                  "createdAt": 1739923200000,
                  "updatedAt": 1740013200000
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. The phoneNumber query parameter is missing or is not a valid phone number."
          },
          "401": {
            "description": "Unauthorized. The request did not include a correct authorization token."
          },
          "404": {
            "description": "Not Found. No contact matched the provided phone number."
          },
          "500": {
            "description": "Internal Server Error. Something went wrong on the server side."
          }
        }
      },
      "post": {
        "tags": [
          "Contact"
        ],
        "summary": "Create a new contact",
        "operationId": "createContact",
        "description": "Creates a contact. Pass 'utm' to record which ad or campaign the lead came from. Returns 400 if the contact already exists; use PUT /contact to update it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactRequest"
              },
              "example": {
                "name": "Jane Doe",
                "phoneNumber": "+14155551234",
                "email": "jane@example.com",
                "source": "website",
                "utm": {
                  "source": "google",
                  "medium": "cpc",
                  "campaign": "diwali_sale",
                  "term": "running shoes",
                  "content": "banner_a",
                  "gclid": "Cj0KCQjw..."
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Contact created successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. The request was unacceptable, often due to missing a required parameter."
          },
          "401": {
            "description": "Unauthorized. The request did not include a correct authorization token."
          },
          "500": {
            "description": "Internal Server Error. Something went wrong on the server side."
          }
        }
      },
      "put": {
        "tags": [
          "Contact"
        ],
        "summary": "Update a contact by phone number",
        "operationId": "updateContactByPhoneNumber",
        "description": "Looks up a contact by phone number and applies the same partial update semantics as PUT /contact/{contactId}. The phone number is normalized: '+' is auto-prepended if missing and the value must contain at least 10 digits. The lookup value can be passed via the 'phoneNumber' query parameter or the 'phoneNumber' field in the request body. Empty fields are ignored — only fields you provide are updated. Pass createIfContactNotPresent=true to create the contact from the body when no contact matches (same as POST /contact).",
        "parameters": [
          {
            "name": "phoneNumber",
            "in": "query",
            "required": false,
            "description": "Phone number to look up the contact by. Falls back to the body's 'phoneNumber' field when omitted. '+' is auto-prepended if missing; minimum 10 digits.",
            "schema": {
              "type": "string"
            },
            "example": "14155551234"
          },
          {
            "name": "createIfContactNotPresent",
            "in": "query",
            "required": false,
            "description": "When true and no contact matches the phone number, a new contact is created from the request body instead of returning 404.",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "example": true
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactRequest"
              },
              "examples": {
                "setStatusAndTagsByName": {
                  "summary": "Set status and tags using names (resolved to IDs)",
                  "value": {
                    "statusName": "Qualified",
                    "tagNames": [
                      "VIP",
                      "Newsletter"
                    ]
                  }
                },
                "setStatusByIdMinimal": {
                  "summary": "Set status by ID, leave everything else untouched",
                  "value": {
                    "status": "status_abc123"
                  }
                },
                "lookupViaBody": {
                  "summary": "Lookup via body (no query param) and update name + group",
                  "value": {
                    "phoneNumber": "14155551234",
                    "name": "Jane Doe",
                    "groupName": "Sales"
                  }
                },
                "fullUpdate": {
                  "summary": "Update most fields at once",
                  "value": {
                    "name": "Jane Doe",
                    "email": "jane@example.com",
                    "message": "Met at trade show",
                    "statusName": "Qualified",
                    "groupName": "Sales",
                    "source": "tradeshow",
                    "eventAt": 1740009600000,
                    "tagNames": [
                      "VIP",
                      "Newsletter"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Contact updated successfully, or created when createIfContactNotPresent=true and no contact matched.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactResponse"
                },
                "example": {
                  "id": "contact_xyz789",
                  "externalId": "crm-1029",
                  "name": "jane doe",
                  "phoneNumber": "+14155551234",
                  "email": "jane@example.com",
                  "message": "Met at trade show",
                  "status": "status_abc123",
                  "group": "group_sales",
                  "source": "tradeshow",
                  "eventAt": 1740009600000,
                  "tags": [
                    "tag_vip",
                    "tag_newsletter"
                  ],
                  "createdAt": 1739923200000,
                  "updatedAt": 1740013200000
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. Invalid phone number, or a provided statusName / groupName / tagNames value did not match any configured entry."
          },
          "401": {
            "description": "Unauthorized. The request did not include a correct authorization token."
          },
          "404": {
            "description": "Not Found. No contact matched the provided phone number."
          },
          "500": {
            "description": "Internal Server Error. Something went wrong on the server side."
          }
        }
      }
    },
    "/api/v1/contact/:contactId": {
      "get": {
        "tags": [
          "Contact"
        ],
        "summary": "Get a contact by ID",
        "operationId": "getContactById",
        "description": "Fetches a single contact by its Wylto contact ID.",
        "parameters": [
          {
            "name": "contactId",
            "in": "path",
            "required": true,
            "description": "The ID of the contact to fetch.",
            "schema": {
              "type": "string"
            },
            "example": "contact_xyz789"
          }
        ],
        "responses": {
          "200": {
            "description": "Contact fetched successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactResponse"
                },
                "example": {
                  "id": "contact_xyz789",
                  "externalId": "crm-1029",
                  "name": "jane doe",
                  "phoneNumber": "+14155551234",
                  "email": "jane@example.com",
                  "message": "Met at trade show",
                  "status": "status_abc123",
                  "group": "group_sales",
                  "source": "tradeshow",
                  "eventAt": 1740009600000,
                  "tags": [
                    "tag_vip",
                    "tag_newsletter"
                  ],
                  "createdAt": 1739923200000,
                  "updatedAt": 1740013200000
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. The contactId path parameter is missing or empty."
          },
          "401": {
            "description": "Unauthorized. The request did not include a correct authorization token."
          },
          "404": {
            "description": "Not Found. No contact matched the provided ID."
          },
          "500": {
            "description": "Internal Server Error. Something went wrong on the server side."
          }
        }
      },
      "put": {
        "tags": [
          "Contact"
        ],
        "summary": "Update a contact",
        "operationId": "updateContact",
        "description": "Partial update for an existing contact identified by ID. Only the fields you include are written; empty strings and empty arrays are ignored. Use 'statusName' / 'groupName' / 'tagNames' to look up IDs by name (case-insensitive); the ID-form fields ('status' / 'group' / 'tags') take precedence when both are provided.",
        "parameters": [
          {
            "name": "contactId",
            "in": "path",
            "required": true,
            "description": "The ID of the contact to update.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactRequest"
              },
              "examples": {
                "setStatusAndTagsByName": {
                  "summary": "Set status and tags using names (resolved to IDs)",
                  "value": {
                    "statusName": "Qualified",
                    "tagNames": [
                      "VIP",
                      "Newsletter"
                    ]
                  }
                },
                "setStatusByIdMinimal": {
                  "summary": "Set status by ID, leave everything else untouched",
                  "value": {
                    "status": "status_abc123"
                  }
                },
                "renameAndChangeGroup": {
                  "summary": "Rename and move to a different group",
                  "value": {
                    "name": "Jane Doe",
                    "groupName": "Sales"
                  }
                },
                "fullUpdate": {
                  "summary": "Update most fields at once",
                  "value": {
                    "name": "Jane Doe",
                    "email": "jane@example.com",
                    "phoneNumber": "+14155551234",
                    "message": "Met at trade show",
                    "statusName": "Qualified",
                    "groupName": "Sales",
                    "source": "tradeshow",
                    "eventAt": 1740009600000,
                    "tagNames": [
                      "VIP",
                      "Newsletter"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Contact updated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactResponse"
                },
                "example": {
                  "id": "contact_xyz789",
                  "externalId": "crm-1029",
                  "name": "jane doe",
                  "phoneNumber": "+14155551234",
                  "email": "jane@example.com",
                  "message": "Met at trade show",
                  "status": "status_abc123",
                  "group": "group_sales",
                  "source": "tradeshow",
                  "eventAt": 1740009600000,
                  "tags": [
                    "tag_vip",
                    "tag_newsletter"
                  ],
                  "createdAt": 1739923200000,
                  "updatedAt": 1740013200000
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. A provided statusName / groupName / tagNames value did not match any configured entry, or another field failed validation."
          },
          "401": {
            "description": "Unauthorized. The request did not include a correct authorization token."
          },
          "500": {
            "description": "Internal Server Error. Something went wrong on the server side."
          }
        }
      },
      "delete": {
        "tags": [
          "Contact"
        ],
        "summary": "Delete a contact",
        "operationId": "deleteContact",
        "parameters": [
          {
            "name": "contactId",
            "in": "path",
            "required": true,
            "description": "The ID of the contact to delete.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Contact deleted successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Indicates if the contact was deleted successfully."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. The request was unacceptable, often due to missing a required parameter."
          },
          "401": {
            "description": "Unauthorized. The request did not include a correct authorization token."
          },
          "500": {
            "description": "Internal Server Error. Something went wrong on the server side."
          }
        }
      }
    },
    "/api/v1/contact/:contactId/activities": {
      "get": {
        "tags": [
          "Contact"
        ],
        "summary": "Get activities for a contact",
        "operationId": "getContactActivities",
        "description": "Returns a contact's activity history (status changes, notes, call logs, chats, broadcasts and more), newest first.",
        "parameters": [
          {
            "name": "contactId",
            "in": "path",
            "required": true,
            "description": "The ID of the contact.",
            "schema": {
              "type": "string"
            },
            "example": "contact_xyz789"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of activities to return. Default 50, max 200.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Activities fetched successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "activities": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ActivityResponse"
                      }
                    }
                  }
                },
                "example": {
                  "activities": [
                    {
                      "id": "act_123",
                      "contactId": "contact_xyz789",
                      "type": "STATUS_UPDATE",
                      "from": "status_new",
                      "to": "status_won",
                      "author": "member_abc",
                      "createdAt": 1740013200000
                    },
                    {
                      "id": "act_122",
                      "contactId": "contact_xyz789",
                      "type": "NOTE",
                      "note": "Asked for a demo next week",
                      "author": "member_abc",
                      "createdAt": 1740009600000
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. contactId is missing or limit is out of range."
          },
          "401": {
            "description": "Unauthorized. The request did not include a correct authorization token."
          },
          "404": {
            "description": "Not Found. No contact matched the provided ID."
          },
          "500": {
            "description": "Internal Server Error. Something went wrong on the server side."
          }
        }
      }
    },
    "/api/v1/wa/templates": {
      "get": {
        "tags": [
          "WhatsApp"
        ],
        "summary": "List WhatsApp templates",
        "operationId": "listTemplates",
        "description": "Returns all WhatsApp message templates for the app's default connection.",
        "responses": {
          "200": {
            "description": "Templates retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WhatsAppTemplate"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized."
          },
          "500": {
            "description": "Internal Server Error."
          }
        }
      },
      "post": {
        "tags": [
          "WhatsApp"
        ],
        "summary": "Create a WhatsApp template",
        "operationId": "createTemplate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTemplateRequest"
              },
              "examples": {
                "marketingTemplate": {
                  "summary": "Create a marketing template",
                  "value": {
                    "name": "welcome_message",
                    "language": "en",
                    "category": "MARKETING",
                    "components": [
                      {
                        "type": "HEADER",
                        "format": "TEXT",
                        "text": "Welcome to {{1}}!"
                      },
                      {
                        "type": "BODY",
                        "text": "Hi {{1}}, thank you for joining us. We're excited to have you!"
                      },
                      {
                        "type": "FOOTER",
                        "text": "Reply STOP to unsubscribe"
                      },
                      {
                        "type": "BUTTONS",
                        "buttons": [
                          {
                            "type": "QUICK_REPLY",
                            "text": "Learn More"
                          }
                        ]
                      }
                    ]
                  }
                },
                "utilityTemplate": {
                  "summary": "Create a utility template",
                  "value": {
                    "name": "order_confirmation",
                    "language": "en",
                    "category": "UTILITY",
                    "components": [
                      {
                        "type": "BODY",
                        "text": "Your order #{{1}} has been confirmed. Expected delivery: {{2}}"
                      }
                    ]
                  }
                },
                "imageHeaderTemplate": {
                  "summary": "Create a template with an image header (from a URL)",
                  "description": "For media headers, pass the public image URL in `mediaUrl` on the HEADER component. The server uploads it to WhatsApp and fills in the media handle for you.",
                  "value": {
                    "name": "image_promo",
                    "language": "en_US",
                    "category": "MARKETING",
                    "components": [
                      {
                        "type": "HEADER",
                        "format": "IMAGE",
                        "mediaUrl": "https://your-cdn.com/banner.jpg"
                      },
                      {
                        "type": "BODY",
                        "text": "Hi {{1}}, check out our latest offer!",
                        "example": {
                          "body_text": [
                            [
                              "Sanath"
                            ]
                          ]
                        }
                      },
                      {
                        "type": "FOOTER",
                        "text": "Reply STOP to unsubscribe"
                      }
                    ]
                  }
                },
                "videoHeaderTemplate": {
                  "summary": "Create a template with a video header (from a URL)",
                  "description": "Same as the image header, but with `format: VIDEO` and a video URL in `mediaUrl`.",
                  "value": {
                    "name": "video_promo",
                    "language": "en_US",
                    "category": "MARKETING",
                    "components": [
                      {
                        "type": "HEADER",
                        "format": "VIDEO",
                        "mediaUrl": "https://your-cdn.com/promo.mp4"
                      },
                      {
                        "type": "BODY",
                        "text": "Watch our latest product demo, {{1}}!",
                        "example": {
                          "body_text": [
                            [
                              "Sanath"
                            ]
                          ]
                        }
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Template created successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateTemplateResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. Invalid template format or missing required fields."
          },
          "401": {
            "description": "Unauthorized."
          },
          "500": {
            "description": "Internal Server Error."
          }
        }
      }
    },
    "/api/v1/wa/templates/:templateId": {
      "put": {
        "tags": [
          "WhatsApp"
        ],
        "summary": "Update a WhatsApp template",
        "operationId": "updateTemplate",
        "description": "Replaces the template's components. Name, language and category can't be changed. Meta reviews the template again after an edit, and limits how often an approved template can be edited.",
        "parameters": [
          {
            "name": "templateId",
            "in": "path",
            "required": true,
            "description": "The WhatsApp template id (the `id` returned by Create or List templates).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "components"
                ],
                "properties": {
                  "components": {
                    "type": "array",
                    "description": "The full new list of components, same format as Create template. For media headers, pass the URL in `mediaUrl`.",
                    "items": {
                      "type": "object"
                    }
                  }
                }
              },
              "examples": {
                "updateBody": {
                  "summary": "Change the body text",
                  "value": {
                    "components": [
                      {
                        "type": "BODY",
                        "text": "Your order #{{1}} is confirmed. It will reach you by {{2}}.",
                        "example": {
                          "body_text": [
                            [
                              "1001",
                              "Friday"
                            ]
                          ]
                        }
                      }
                    ]
                  }
                },
                "updateImageHeader": {
                  "summary": "Change the header image",
                  "value": {
                    "components": [
                      {
                        "type": "HEADER",
                        "format": "IMAGE",
                        "mediaUrl": "https://your-cdn.com/new-banner.jpg"
                      },
                      {
                        "type": "BODY",
                        "text": "Hi {{1}}, check out our latest offer!",
                        "example": {
                          "body_text": [
                            [
                              "Sanath"
                            ]
                          ]
                        }
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Template updated.",
            "content": {
              "application/json": {
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. Invalid components, or Meta rejected the edit."
          },
          "401": {
            "description": "Unauthorized."
          },
          "500": {
            "description": "Internal Server Error."
          }
        }
      }
    },
    "/api/v1/wa/templates/:templateName": {
      "delete": {
        "tags": [
          "WhatsApp"
        ],
        "summary": "Delete a WhatsApp template",
        "operationId": "deleteTemplate",
        "description": "Deletes the template by name. This deletes every language of that template. A deleted name can't be reused for 30 days.",
        "parameters": [
          {
            "name": "templateName",
            "in": "path",
            "required": true,
            "description": "The template name, e.g. `order_confirmation`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Template deleted.",
            "content": {
              "application/json": {
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. Template not found or Meta rejected the delete."
          },
          "401": {
            "description": "Unauthorized."
          },
          "500": {
            "description": "Internal Server Error."
          }
        }
      }
    },
    "/api/v1/wa/templates/:templateId/quality": {
      "get": {
        "tags": [
          "WhatsApp"
        ],
        "summary": "Get template quality score",
        "operationId": "getTemplateQuality",
        "parameters": [
          {
            "name": "templateId",
            "in": "path",
            "required": true,
            "description": "The ID of the template.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Template quality retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateQualityResponse"
                },
                "example": {
                  "id": "123456789",
                  "name": "welcome_message",
                  "status": "APPROVED",
                  "qualityScore": "GREEN"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized."
          },
          "404": {
            "description": "Template not found."
          },
          "500": {
            "description": "Internal Server Error."
          }
        }
      }
    },
    "/api/v1/wa/phone-number": {
      "get": {
        "tags": [
          "WhatsApp"
        ],
        "summary": "Get phone number details",
        "operationId": "getPhoneNumberDetails",
        "description": "Returns details about the WhatsApp phone number including quality rating and messaging limits.",
        "responses": {
          "200": {
            "description": "Phone number details retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumberDetailsResponse"
                },
                "example": {
                  "verified_name": "My Business",
                  "display_phone_number": "+1234567890",
                  "quality_rating": "GREEN",
                  "messaging_limit_tier": "TIER_10K",
                  "platform_type": "CLOUD_API",
                  "throughput": {
                    "level": "STANDARD"
                  },
                  "code_verification_status": "VERIFIED",
                  "last_onboarded_time": "2024-01-01T00:00:00Z",
                  "id": "180876681785984"
                }
              }
            }
          },
          "400": {
            "description": "WhatsApp connection not found."
          },
          "401": {
            "description": "Unauthorized."
          },
          "500": {
            "description": "Internal Server Error."
          }
        }
      }
    },
    "/api/v1/campaigns": {
      "post": {
        "tags": [
          "Campaigns"
        ],
        "summary": "Create and send a campaign",
        "operationId": "createCampaign",
        "description": "Creates a campaign and sends messages to the provided contacts. Only CSV type campaigns are supported via API.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCampaignRequest"
              },
              "examples": {
                "templateCampaign": {
                  "summary": "Send template to multiple contacts",
                  "value": {
                    "campaignName": "January Promo",
                    "contacts": [
                      {
                        "name": "John Doe",
                        "phoneNumber": "+1234567890"
                      },
                      {
                        "name": "Jane Smith",
                        "phoneNumber": "+0987654321"
                      }
                    ],
                    "message": {
                      "type": "template",
                      "template": {
                        "templateName": "promo_offer",
                        "language": "en",
                        "body": [
                          {
                            "type": "text",
                            "text": "20%"
                          }
                        ]
                      }
                    }
                  }
                },
                "mmLiteCampaign": {
                  "summary": "Send with Marketing Messages Lite",
                  "value": {
                    "campaignName": "Flash Sale",
                    "contacts": [
                      {
                        "name": "John Doe",
                        "phoneNumber": "+1234567890"
                      }
                    ],
                    "message": {
                      "type": "template",
                      "template": {
                        "templateName": "flash_sale",
                        "language": "en"
                      }
                    },
                    "isMMLite": true,
                    "ttl": 86400
                  }
                },
                "headerImageCampaign": {
                  "summary": "Send template with a header image",
                  "description": "For a template with an IMAGE header. Pass a public JPG or PNG link in `header.image.link`. `{{$name}}` in body text is filled with each contact's first name.",
                  "value": {
                    "campaignName": "Diwali Sale",
                    "contacts": [
                      {
                        "name": "John Doe",
                        "phoneNumber": "+1234567890"
                      },
                      {
                        "name": "Priya Sharma",
                        "phoneNumber": "+919876543210"
                      }
                    ],
                    "message": {
                      "type": "template",
                      "template": {
                        "templateName": "diwali_offer",
                        "language": "en",
                        "header": {
                          "type": "image",
                          "image": {
                            "link": "https://example.com/images/diwali-banner.jpg"
                          }
                        },
                        "body": [
                          {
                            "type": "text",
                            "text": "{{$name}}"
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Campaign created and processing started.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. Invalid campaign data."
          },
          "401": {
            "description": "Unauthorized."
          },
          "500": {
            "description": "Internal Server Error."
          }
        }
      }
    },
    "/api/v1/campaigns/:campaignId/report": {
      "get": {
        "tags": [
          "Campaigns"
        ],
        "summary": "Get campaign report",
        "operationId": "getCampaignReport",
        "description": "Returns campaign details and all message delivery statuses.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "The ID of the campaign.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Campaign report retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignReportResponse"
                },
                "example": {
                  "campaign": {
                    "id": "abc123",
                    "name": "January Promo",
                    "type": "csv",
                    "status": "completed",
                    "channel": "whatsapp",
                    "count": 100,
                    "sent": 95,
                    "delivered": 90,
                    "read": 50,
                    "failed": 5,
                    "createdAt": 1704067200000
                  },
                  "messages": [
                    {
                      "id": "msg123",
                      "name": "John Doe",
                      "phoneNumber": "+1234567890",
                      "state": "delivered",
                      "buttonClicks": [],
                      "createdAt": 1704067200000
                    },
                    {
                      "id": "msg124",
                      "name": "Jane Smith",
                      "phoneNumber": "+0987654321",
                      "state": "failed",
                      "error": "131026- Message undeliverable",
                      "buttonClicks": [],
                      "createdAt": 1704067200000
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized."
          },
          "404": {
            "description": "Campaign not found."
          },
          "500": {
            "description": "Internal Server Error."
          }
        }
      }
    }
  },
  "webhooks": {
    "on new Message": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "description": "When you subscribe for a message webhook,Wylto sends this payload when a new message is received.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OnNewMessagePayload"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Your server response after receiving the event"
          }
        }
      }
    },
    "Booking created": {
      "post": {
        "tags": [
          "Booking webhooks"
        ],
        "security": [],
        "summary": "Booking created",
        "description": "Sent when someone books a slot: from a booking page, an embed, the chatbot or the AI agent.\n\nSet up under **Appointments → Settings → Webhooks** in Wylto: add an HTTPS URL, optional custom headers and an optional signing secret, pick the events, and choose all booking pages or some. Wylto sends a JSON `POST`. Reply with any 2xx status within 10 seconds; failed deliveries are not retried and show in the deliveries log.",
        "parameters": [
          {
            "name": "X-Wylto-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Same as triggerEvent."
          },
          {
            "name": "X-Wylto-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique id of this delivery. Use it to ignore duplicates."
          },
          {
            "name": "X-Wylto-Signature-256",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only when the webhook has a secret: `sha256=<hex>`, the HMAC-SHA256 of the raw request body with your secret. Compute it yourself and compare before trusting the request."
          },
          {
            "name": "User-Agent",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "example": "Wylto-Webhooks/1.0"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BookingWebhook"
              },
              "example": {
                "triggerEvent": "BOOKING_CREATED",
                "createdAt": "2026-11-20T10:02:11Z",
                "payload": {
                  "bookingId": "HRXLZixYtwudTxclPcbE",
                  "eventTypeId": "yJ08Ls8T4CuOk2ZNhFFc",
                  "eventTypeName": "Product demo",
                  "status": "scheduled",
                  "startTime": "2026-11-24T05:30:00Z",
                  "endTime": "2026-11-24T06:00:00Z",
                  "duration": 30,
                  "timeZone": "Asia/Kolkata",
                  "location": "video",
                  "meetingLink": "https://meet.google.com/abc-defg-hij",
                  "contact": {
                    "id": "k2VdN8pQ1xYz",
                    "name": "Asha",
                    "email": "asha@example.com",
                    "phone": "919800000000"
                  },
                  "assignedTo": {
                    "id": "u7Ty3Lm9",
                    "name": "Priya",
                    "email": "priya@yourcompany.com"
                  },
                  "responses": {
                    "name": "Asha",
                    "email": "asha@example.com",
                    "phone": "919800000000",
                    "notes": "Interested in the Growth plan"
                  },
                  "source": "event-booking",
                  "utm": {
                    "source": "facebook",
                    "medium": "paid_social",
                    "campaign": "diwali_sale"
                  },
                  "rescheduleUrl": "https://app.wylto.com/booking-manage?…",
                  "cancelUrl": "https://app.wylto.com/booking-manage?…"
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any 2xx counts as delivered."
          }
        }
      }
    },
    "Booking rescheduled": {
      "post": {
        "tags": [
          "Booking webhooks"
        ],
        "security": [],
        "summary": "Booking rescheduled",
        "description": "Sent when the booker or your team moves a booking. `previousStartTime` has the old start.\n\nSet up under **Appointments → Settings → Webhooks** in Wylto: add an HTTPS URL, optional custom headers and an optional signing secret, pick the events, and choose all booking pages or some. Wylto sends a JSON `POST`. Reply with any 2xx status within 10 seconds; failed deliveries are not retried and show in the deliveries log.",
        "parameters": [
          {
            "name": "X-Wylto-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Same as triggerEvent."
          },
          {
            "name": "X-Wylto-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique id of this delivery. Use it to ignore duplicates."
          },
          {
            "name": "X-Wylto-Signature-256",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only when the webhook has a secret: `sha256=<hex>`, the HMAC-SHA256 of the raw request body with your secret. Compute it yourself and compare before trusting the request."
          },
          {
            "name": "User-Agent",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "example": "Wylto-Webhooks/1.0"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BookingWebhook"
              },
              "example": {
                "triggerEvent": "BOOKING_RESCHEDULED",
                "createdAt": "2026-11-20T10:02:11Z",
                "payload": {
                  "bookingId": "HRXLZixYtwudTxclPcbE",
                  "eventTypeId": "yJ08Ls8T4CuOk2ZNhFFc",
                  "eventTypeName": "Product demo",
                  "status": "rescheduled",
                  "startTime": "2026-11-25T09:00:00Z",
                  "endTime": "2026-11-25T09:30:00Z",
                  "duration": 30,
                  "timeZone": "Asia/Kolkata",
                  "location": "video",
                  "meetingLink": "https://meet.google.com/abc-defg-hij",
                  "contact": {
                    "id": "k2VdN8pQ1xYz",
                    "name": "Asha",
                    "email": "asha@example.com",
                    "phone": "919800000000"
                  },
                  "assignedTo": {
                    "id": "u7Ty3Lm9",
                    "name": "Priya",
                    "email": "priya@yourcompany.com"
                  },
                  "responses": {
                    "name": "Asha",
                    "email": "asha@example.com",
                    "phone": "919800000000",
                    "notes": "Interested in the Growth plan"
                  },
                  "source": "event-booking",
                  "utm": {
                    "source": "facebook",
                    "medium": "paid_social",
                    "campaign": "diwali_sale"
                  },
                  "rescheduleUrl": "https://app.wylto.com/booking-manage?…",
                  "cancelUrl": "https://app.wylto.com/booking-manage?…",
                  "previousStartTime": "2026-11-24T05:30:00Z"
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any 2xx counts as delivered."
          }
        }
      }
    },
    "Booking cancelled": {
      "post": {
        "tags": [
          "Booking webhooks"
        ],
        "security": [],
        "summary": "Booking cancelled",
        "description": "Sent when the booker or your team cancels a booking.\n\nSet up under **Appointments → Settings → Webhooks** in Wylto: add an HTTPS URL, optional custom headers and an optional signing secret, pick the events, and choose all booking pages or some. Wylto sends a JSON `POST`. Reply with any 2xx status within 10 seconds; failed deliveries are not retried and show in the deliveries log.",
        "parameters": [
          {
            "name": "X-Wylto-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Same as triggerEvent."
          },
          {
            "name": "X-Wylto-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique id of this delivery. Use it to ignore duplicates."
          },
          {
            "name": "X-Wylto-Signature-256",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only when the webhook has a secret: `sha256=<hex>`, the HMAC-SHA256 of the raw request body with your secret. Compute it yourself and compare before trusting the request."
          },
          {
            "name": "User-Agent",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "example": "Wylto-Webhooks/1.0"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BookingWebhook"
              },
              "example": {
                "triggerEvent": "BOOKING_CANCELLED",
                "createdAt": "2026-11-20T10:02:11Z",
                "payload": {
                  "bookingId": "HRXLZixYtwudTxclPcbE",
                  "eventTypeId": "yJ08Ls8T4CuOk2ZNhFFc",
                  "eventTypeName": "Product demo",
                  "status": "cancelled",
                  "startTime": "2026-11-24T05:30:00Z",
                  "endTime": "2026-11-24T06:00:00Z",
                  "duration": 30,
                  "timeZone": "Asia/Kolkata",
                  "location": "video",
                  "meetingLink": "https://meet.google.com/abc-defg-hij",
                  "contact": {
                    "id": "k2VdN8pQ1xYz",
                    "name": "Asha",
                    "email": "asha@example.com",
                    "phone": "919800000000"
                  },
                  "assignedTo": {
                    "id": "u7Ty3Lm9",
                    "name": "Priya",
                    "email": "priya@yourcompany.com"
                  },
                  "responses": {
                    "name": "Asha",
                    "email": "asha@example.com",
                    "phone": "919800000000",
                    "notes": "Interested in the Growth plan"
                  },
                  "source": "event-booking",
                  "utm": {
                    "source": "facebook",
                    "medium": "paid_social",
                    "campaign": "diwali_sale"
                  },
                  "rescheduleUrl": "https://app.wylto.com/booking-manage?…",
                  "cancelUrl": "https://app.wylto.com/booking-manage?…"
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any 2xx counts as delivered."
          }
        }
      }
    },
    "Booking completed": {
      "post": {
        "tags": [
          "Booking webhooks"
        ],
        "security": [],
        "summary": "Booking completed",
        "description": "Sent when your team marks a booking as completed. `minutesOfMeeting` has the notes, if any.\n\nSet up under **Appointments → Settings → Webhooks** in Wylto: add an HTTPS URL, optional custom headers and an optional signing secret, pick the events, and choose all booking pages or some. Wylto sends a JSON `POST`. Reply with any 2xx status within 10 seconds; failed deliveries are not retried and show in the deliveries log.",
        "parameters": [
          {
            "name": "X-Wylto-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Same as triggerEvent."
          },
          {
            "name": "X-Wylto-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique id of this delivery. Use it to ignore duplicates."
          },
          {
            "name": "X-Wylto-Signature-256",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only when the webhook has a secret: `sha256=<hex>`, the HMAC-SHA256 of the raw request body with your secret. Compute it yourself and compare before trusting the request."
          },
          {
            "name": "User-Agent",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "example": "Wylto-Webhooks/1.0"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BookingWebhook"
              },
              "example": {
                "triggerEvent": "BOOKING_COMPLETED",
                "createdAt": "2026-11-20T10:02:11Z",
                "payload": {
                  "bookingId": "HRXLZixYtwudTxclPcbE",
                  "eventTypeId": "yJ08Ls8T4CuOk2ZNhFFc",
                  "eventTypeName": "Product demo",
                  "status": "completed",
                  "startTime": "2026-11-24T05:30:00Z",
                  "endTime": "2026-11-24T06:00:00Z",
                  "duration": 30,
                  "timeZone": "Asia/Kolkata",
                  "location": "video",
                  "meetingLink": "https://meet.google.com/abc-defg-hij",
                  "contact": {
                    "id": "k2VdN8pQ1xYz",
                    "name": "Asha",
                    "email": "asha@example.com",
                    "phone": "919800000000"
                  },
                  "assignedTo": {
                    "id": "u7Ty3Lm9",
                    "name": "Priya",
                    "email": "priya@yourcompany.com"
                  },
                  "responses": {
                    "name": "Asha",
                    "email": "asha@example.com",
                    "phone": "919800000000",
                    "notes": "Interested in the Growth plan"
                  },
                  "source": "event-booking",
                  "utm": {
                    "source": "facebook",
                    "medium": "paid_social",
                    "campaign": "diwali_sale"
                  },
                  "rescheduleUrl": "https://app.wylto.com/booking-manage?…",
                  "cancelUrl": "https://app.wylto.com/booking-manage?…",
                  "minutesOfMeeting": "Wants the Growth plan. Send pricing by Friday."
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any 2xx counts as delivered."
          }
        }
      }
    },
    "Booking webhook test ping": {
      "post": {
        "tags": [
          "Booking webhooks"
        ],
        "security": [],
        "summary": "Test ping",
        "description": "Sent when you press **Send test** on a webhook. Sample data only.\n\nSet up under **Appointments → Settings → Webhooks** in Wylto: add an HTTPS URL, optional custom headers and an optional signing secret, pick the events, and choose all booking pages or some. Wylto sends a JSON `POST`. Reply with any 2xx status within 10 seconds; failed deliveries are not retried and show in the deliveries log.",
        "parameters": [
          {
            "name": "X-Wylto-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Same as triggerEvent."
          },
          {
            "name": "X-Wylto-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique id of this delivery. Use it to ignore duplicates."
          },
          {
            "name": "X-Wylto-Signature-256",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only when the webhook has a secret: `sha256=<hex>`, the HMAC-SHA256 of the raw request body with your secret. Compute it yourself and compare before trusting the request."
          },
          {
            "name": "User-Agent",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "example": "Wylto-Webhooks/1.0"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BookingWebhook"
              },
              "example": {
                "triggerEvent": "PING",
                "createdAt": "2026-11-20T10:02:11Z",
                "payload": {
                  "bookingId": "test",
                  "eventTypeName": "Test booking",
                  "status": "scheduled",
                  "startTime": "2026-11-24T05:30:00Z",
                  "endTime": "2026-11-24T06:00:00Z",
                  "duration": 30,
                  "contact": {
                    "name": "Test contact",
                    "email": "test@example.com",
                    "phone": "919999999999"
                  },
                  "utm": {
                    "source": "facebook",
                    "medium": "paid_social",
                    "campaign": "test_campaign"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any 2xx counts as delivered."
          }
        }
      }
    }
  }
}