{
  "openapi": "3.0.3",
  "info": {
    "title": "VelBEE lead intake",
    "version": "1.0.0",
    "description": "Send enquiries from a property portal (or any system) to one VelBEE customer's workspace.\n\nEach customer has a secret address per portal: `https://api.velbee.com/v1/ingest/{source}/{secret}`. The secret is part of the path and is the only credential: there is no API key, header or login. Treat the whole address as a password and keep it out of logs.\n\nAn enquiry needs a phone number (at least 6 digits) or a valid email address; everything else is optional. Field names are matched loosely (upper or lower case, dashes, spaces and camelCase are all fine), and anything not recognised is kept in the lead's note. Send the portal's own enquiry id so repeats never create a second lead.\n\nTechnical sheet: https://velbee.com/resources/portal-integration"
  },
  "externalDocs": {
    "description": "Technical sheet for portals and their developers",
    "url": "https://velbee.com/resources/portal-integration"
  },
  "servers": [
    {
      "url": "https://api.velbee.com",
      "description": "VelBEE"
    }
  ],
  "tags": [
    {
      "name": "Lead intake"
    }
  ],
  "paths": {
    "/v1/ingest/{source}/{secret}": {
      "post": {
        "tags": [
          "Lead intake"
        ],
        "operationId": "sendEnquiries",
        "summary": "Send one enquiry, or a list of up to 100",
        "description": "Creates a lead in the customer's workspace for each enquiry. The same enquiry id from the same address is accepted once; a repeat answers `duplicate: true`. The customer can set the success code to 200 (the default for portals), 201 or 202, so treat any 2xx as success. Requests larger than 64 KB are refused with 413. Allow a client timeout of at least 10 seconds.\n\nSecurity: the secret in the path is the credential. Do not log full URLs.",
        "parameters": [
          {
            "name": "source",
            "in": "path",
            "required": true,
            "description": "The source part of the address, for example `src_xxxxxxxxxxxxxxxxxxxx`.",
            "schema": {
              "type": "string",
              "pattern": "^src_[a-z0-9]+$"
            },
            "example": "src_xxxxxxxxxxxxxxxxxxxx"
          },
          {
            "name": "secret",
            "in": "path",
            "required": true,
            "description": "The secret part of the address, for example `vbu_xxxxxxxxxxxxxxxxxxxx`. It is the credential.",
            "schema": {
              "type": "string",
              "pattern": "^vbu_[A-Za-z0-9]+$"
            },
            "example": "vbu_xxxxxxxxxxxxxxxxxxxx"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/Enquiry"
                  },
                  {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 100,
                    "items": {
                      "$ref": "#/components/schemas/Enquiry"
                    }
                  }
                ]
              },
              "examples": {
                "one": {
                  "summary": "One enquiry",
                  "value": {
                    "enquiry_id": "PORTAL-20261008-000123",
                    "name": "Asha Verma",
                    "mobile": "+919876543210",
                    "email": "asha@example.com",
                    "project_name": "Lakeview Residences, 3 BHK",
                    "city": "Hyderabad",
                    "budget": "1.2 Cr",
                    "enquired_at": "2026-10-08T14:32:10+05:30",
                    "message": "Please call after 6 pm"
                  }
                },
                "list": {
                  "summary": "A list of enquiries (up to 100)",
                  "value": [
                    {
                      "enquiry_id": "A1",
                      "name": "Ravi Kumar",
                      "mobile": "9811100001"
                    },
                    {
                      "enquiry_id": "A2",
                      "name": "Meera Nair",
                      "mobile": "9811100002",
                      "email": "meera@example.com"
                    }
                  ]
                }
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/Enquiry"
              },
              "example": "enquiry_id=PORTAL-20261008-000124&name=Form+Sender&mobile=9811100003&email=form%40example.com&message=Sent+as+form+data"
            }
          }
        },
        "responses": {
          "200": {
            "description": "Accepted. Default success code for a portal (the customer may change it to 201 or 202). A single enquiry answers with the first shape, a list with counts. `duplicate: true` means the enquiry id was already received and nothing new was created: do not retry.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Accepted"
                    },
                    {
                      "$ref": "#/components/schemas/BatchResult"
                    }
                  ]
                },
                "examples": {
                  "one": {
                    "summary": "One enquiry",
                    "value": {
                      "ok": true,
                      "duplicate": false,
                      "message": "Thank you. We have received your details and will be in touch.",
                      "redirect": null
                    }
                  },
                  "duplicate": {
                    "summary": "Repeat of an enquiry id",
                    "value": {
                      "ok": true,
                      "duplicate": true,
                      "message": "Thank you. We have received your details and will be in touch."
                    }
                  },
                  "list": {
                    "summary": "A list",
                    "value": {
                      "ok": true,
                      "received": 3,
                      "created": 3,
                      "duplicate": 0,
                      "refused": 0
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Accepted (when the customer set 201 as the success code). Same body as 200.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Accepted"
                    },
                    {
                      "$ref": "#/components/schemas/BatchResult"
                    }
                  ]
                },
                "example": {
                  "ok": true,
                  "duplicate": false,
                  "message": "Thank you. We have received your details and will be in touch.",
                  "redirect": null
                }
              }
            }
          },
          "202": {
            "description": "Received and held, because the customer's workspace is paused or not accepting leads. It is kept, not lost. Treat as success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Parked"
                },
                "example": {
                  "ok": true,
                  "parked": true,
                  "message": "Thank you. We have received your details and will be in touch."
                }
              }
            }
          },
          "207": {
            "description": "A list where some enquiries were refused. Fix the data and re-send; re-sending the whole list is safe when each enquiry has an id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchResult"
                },
                "example": {
                  "ok": false,
                  "received": 3,
                  "created": 2,
                  "duplicate": 0,
                  "refused": 1
                }
              }
            }
          },
          "401": {
            "description": "Wrong or replaced secret (the customer made a new address). Stop and tell the customer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Not authorised."
                }
              }
            }
          },
          "403": {
            "description": "The customer has paused this address. Stop; the customer can resume it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "This form is switched off."
                }
              }
            }
          },
          "404": {
            "description": "Unknown address, or the secret is not in the expected form. Stop and tell the customer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Unknown source."
                }
              }
            }
          },
          "413": {
            "description": "Request larger than 64 KB. Split it into smaller requests.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "That message is too large."
                }
              }
            }
          },
          "422": {
            "description": "The enquiry has neither a phone number nor an email address. Nothing is saved. Do not retry; fix the data.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                },
                "example": {
                  "message": "Please give a phone number or an email address.",
                  "errors": {
                    "phone": [
                      "A phone number or email is required."
                    ]
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the Retry-After header, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimited"
                },
                "example": {
                  "message": "Too many requests.",
                  "retry_after": 30
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "example": 30
              }
            }
          },
          "5XX": {
            "description": "A problem on VelBEE's side (500), or intake paused for a moment (503). The enquiry id is not used up, so retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Something went wrong on our side. Please try again."
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Enquiry": {
        "type": "object",
        "description": "One enquiry. Needs a phone number or an email address; every other field is optional. Unrecognised fields are kept in the lead's note (up to 15, each cut at 300 characters). Please send flat fields. Do not send a non-empty field called `website`: it marks the request as spam.",
        "additionalProperties": true,
        "properties": {
          "enquiry_id": {
            "type": "string",
            "maxLength": 120,
            "description": "The portal's own enquiry id. Strongly recommended: the same id from the same address creates one lead only, so retries are safe. Also accepted: lead_id, leadid, query_id, reference_id, ref_id, external_id, portal_lead_id, inquiry_id, lead_reference.",
            "example": "PORTAL-20261008-000123"
          },
          "name": {
            "type": "string",
            "maxLength": 160,
            "description": "Full name. Also accepted: full_name, customer_name, contact_name, lead_name, client_name, visitor_name, applicant_name, or first_name with last_name.",
            "example": "Asha Verma"
          },
          "mobile": {
            "type": "string",
            "description": "Phone number, at least 6 digits, with country code if possible. Also accepted: phone, mobile_no, mobile_number, phone_number, contact_no, contact_number, contact, tel, telephone, whatsapp, cell.",
            "example": "+919876543210"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email address. Also accepted: email_address, email_id, e_mail, mail.",
            "example": "asha@example.com"
          },
          "project_name": {
            "type": "string",
            "maxLength": 255,
            "description": "The project or listing the buyer asked about. Also accepted: project, property, property_name, property_title, listing, listing_title, unit, unit_type, property_type, product, interested_in.",
            "example": "Lakeview Residences, 3 BHK"
          },
          "city": {
            "type": "string",
            "description": "Not a lead field of its own: kept in the lead's note with the other extra fields.",
            "example": "Hyderabad"
          },
          "budget": {
            "type": "string",
            "description": "Not a lead field of its own: kept in the lead's note.",
            "example": "1.2 Cr"
          },
          "enquired_at": {
            "type": "string",
            "description": "When the buyer enquired, with a time zone. Kept in the lead's note.",
            "example": "2026-10-08T14:32:10+05:30"
          },
          "message": {
            "type": "string",
            "maxLength": 2000,
            "description": "The buyer's message. Also accepted: comments, description, remarks, requirement, enquiry, inquiry, query, notes, details.",
            "example": "Please call after 6 pm"
          }
        },
        "example": {
          "enquiry_id": "PORTAL-20261008-000123",
          "name": "Asha Verma",
          "mobile": "+919876543210",
          "email": "asha@example.com",
          "project_name": "Lakeview Residences, 3 BHK",
          "city": "Hyderabad",
          "budget": "1.2 Cr",
          "enquired_at": "2026-10-08T14:32:10+05:30",
          "message": "Please call after 6 pm"
        }
      },
      "Accepted": {
        "type": "object",
        "required": [
          "ok",
          "duplicate"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "example": true
          },
          "duplicate": {
            "type": "boolean",
            "description": "True when this enquiry id was already received (or the person already had an open lead); no second lead was made."
          },
          "message": {
            "type": "string"
          },
          "redirect": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "BatchResult": {
        "type": "object",
        "required": [
          "ok",
          "received",
          "created",
          "duplicate",
          "refused"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "False when at least one enquiry in the list was refused (status 207)."
          },
          "received": {
            "type": "integer",
            "description": "How many enquiries were read (at most 100)."
          },
          "created": {
            "type": "integer"
          },
          "duplicate": {
            "type": "integer"
          },
          "refused": {
            "type": "integer"
          }
        }
      },
      "Parked": {
        "type": "object",
        "required": [
          "ok",
          "parked"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "example": true
          },
          "parked": {
            "type": "boolean",
            "example": true
          },
          "message": {
            "type": "string"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "type": "string"
          }
        }
      },
      "ValidationError": {
        "type": "object",
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "type": "string"
          },
          "errors": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        }
      },
      "RateLimited": {
        "type": "object",
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "type": "string"
          },
          "retry_after": {
            "type": "integer",
            "description": "Same as the Retry-After header."
          }
        }
      }
    }
  }
}
