{
  "openapi": "3.1.0",
  "info": {
    "title": "Crmers API",
    "version": "1.0.0",
    "summary": "Ask Crmers, an Israeli CRM consultancy, to call someone back, and search its Hebrew knowledge base.",
    "description": "Public HTTP API of crmers.co.il. Versioning: the consultation API lives under /api/v1/; a breaking change will get a new version path, and /api/v1/ keeps working. No key, no sign-up: every operation is anonymous. Consultation requests are limited to 5 per sender address in 10 minutes, and search to 60 requests a minute. Content is in Hebrew. Guidance for agents, including when Crmers is the right call and how to ask for consent: https://crmers.co.il/agents.md",
    "contact": {
      "name": "Crmers",
      "email": "office@crmers.co.il",
      "url": "https://crmers.co.il/developers/"
    }
  },
  "servers": [
    {
      "url": "https://crmers.co.il"
    }
  ],
  "security": [
    {}
  ],
  "externalDocs": {
    "description": "Guidance for AI agents",
    "url": "https://crmers.co.il/agents.md"
  },
  "tags": [
    {
      "name": "consultation",
      "description": "Consultation requests (leads)."
    },
    {
      "name": "search",
      "description": "Search over the site's pages (NLWeb)."
    }
  ],
  "paths": {
    "/api/v1/lead/": {
      "post": {
        "operationId": "requestConsultation",
        "tags": [
          "consultation"
        ],
        "summary": "Ask Crmers to call a person back for an initial scoping call",
        "description": "Creates a consultation request: Crmers calls the phone number back within the next business hours for an initial scoping call, free and without commitment. Send a person's details only after they asked to be contacted by Crmers and agreed to share them. Set dry_run to validate the request without creating it. At most 5 accepted requests per sender address in 10 minutes.",
        "parameters": [
          {
            "name": "dry_run",
            "in": "query",
            "required": false,
            "description": "Same as the dry_run body field: true, false, 1 or 0.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Any unique string, such as a UUID, sent again on every retry of the same request. A repeat within 24 hours is answered with duplicate: true and the Idempotent-Replayed header, and nothing new is stored or sent.",
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LeadRequest"
              },
              "examples": {
                "test": {
                  "summary": "Validation only: nothing is stored or sent",
                  "value": {
                    "name": "Dana Cohen",
                    "phone": "050-1234567",
                    "dry_run": true
                  }
                },
                "real": {
                  "summary": "Real request: Crmers will call this number",
                  "value": {
                    "name": "Dana Cohen",
                    "phone": "050-1234567",
                    "email": "dana@example.co.il",
                    "company": "Example Ltd",
                    "message": "Moving a 12-person sales team from Excel to a CRM"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The request was received, or validated when dry_run is set.",
            "headers": {
              "Idempotent-Replayed": {
                "description": "true when the Idempotency-Key was already used and this reply repeats the first one.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/LeadAccepted"
                    },
                    {
                      "$ref": "#/components/schemas/LeadDryRun"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The body is not a JSON object (invalid_json), name or phone is missing or too short (invalid_input), or the email is not valid (invalid_email). The fields member names the fields to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Only POST is accepted (method_not_allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "This address already sent 5 requests in the last 10 minutes (too_many_requests), so this one was not saved. Hosted agents often share one address: if this user's request was not sent earlier in that window, give the user +972-72-372-7806 or office@crmers.co.il.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before sending again.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Nothing was saved (server_error). Try again, or call +972-72-372-7806.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Requests are temporarily unavailable (db_not_configured). Call +972-72-372-7806 or email office@crmers.co.il.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/ask/": {
      "post": {
        "operationId": "searchContent",
        "tags": [
          "search"
        ],
        "summary": "Search Crmers' pages with a natural-language query (NLWeb 0.55)",
        "description": "Returns the best-matching pages as schema.org objects, best first. Content is Hebrew, so Hebrew keywords match best; product names such as Zoho or Monday work in English. For a server-sent event stream, set prefer.streaming to true or send Accept: text/event-stream: events are start, one result per page ({index, item}), then complete.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AskRequest"
              },
              "example": {
                "query": {
                  "text": "כמה עולה הטמעת CRM"
                },
                "meta": {
                  "version": "0.55"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Search results, as JSON or as a server-sent event stream.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed per window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AskResponse"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "NLWeb 0.55 events: start, result (one per page), complete."
                }
              }
            }
          },
          "400": {
            "description": "The query is missing or the body is not JSON (INVALID_QUERY).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AskFailure"
                }
              }
            }
          },
          "429": {
            "description": "More than 60 requests a minute from this address (RATE_LIMITED).",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AskFailure"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "searchContentByQuery",
        "tags": [
          "search"
        ],
        "summary": "Search Crmers' pages, query in the URL (older NLWeb clients)",
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": true,
            "description": "The question or keywords.",
            "schema": {
              "type": "string",
              "maxLength": 500
            },
            "example": "Zoho"
          },
          {
            "name": "max_results",
            "in": "query",
            "required": false,
            "description": "How many pages to return, 1 to 20 (default 10).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20
            }
          },
          {
            "name": "streaming",
            "in": "query",
            "required": false,
            "description": "true for a server-sent event stream.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Search results, as JSON or (with streaming=true) as a server-sent event stream.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed per window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AskResponse"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "NLWeb 0.55 events: start, result (one per page), complete."
                }
              }
            }
          },
          "400": {
            "description": "The query is missing (INVALID_QUERY).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AskFailure"
                }
              }
            }
          },
          "429": {
            "description": "More than 60 requests a minute from this address (RATE_LIMITED).",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AskFailure"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "LeadRequest": {
        "type": "object",
        "required": [
          "name",
          "phone"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 2,
            "maxLength": 120,
            "description": "Full name of the person who wants the call."
          },
          "phone": {
            "type": "string",
            "maxLength": 40,
            "description": "Phone number with at least 9 digits (spaces, dashes and + are fine), for example 050-1234567 or +972501234567."
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 254
          },
          "company": {
            "type": "string",
            "maxLength": 160
          },
          "message": {
            "type": "string",
            "maxLength": 2000,
            "description": "What they need, in a sentence or two. Any language."
          },
          "source": {
            "type": "string",
            "enum": [
              "agent",
              "homepage",
              "contact"
            ],
            "default": "agent",
            "description": "Leave out, or send agent. homepage and contact are the site's own forms."
          },
          "page": {
            "type": "string",
            "maxLength": 300,
            "description": "Site path the request relates to, if any, such as /monday/."
          },
          "dry_run": {
            "type": "boolean",
            "default": false,
            "description": "Validate only. Nothing is stored or sent, and it does not count toward the rate limit. Accepted: true or false (also \"true\", \"false\", 1, 0); any other value is rejected with invalid_input."
          }
        }
      },
      "LeadAccepted": {
        "type": "object",
        "required": [
          "ok",
          "message"
        ],
        "properties": {
          "ok": {
            "const": true
          },
          "message": {
            "type": "string"
          },
          "duplicate": {
            "type": "boolean",
            "description": "true when this Idempotency-Key was already received; nothing new was stored."
          }
        }
      },
      "LeadDryRun": {
        "type": "object",
        "required": [
          "ok",
          "dry_run",
          "message",
          "lead"
        ],
        "properties": {
          "ok": {
            "const": true
          },
          "dry_run": {
            "const": true
          },
          "message": {
            "type": "string"
          },
          "lead": {
            "type": "object",
            "description": "The fields as they would be stored, after trimming and length limits."
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error",
          "message"
        ],
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "invalid_json",
              "invalid_input",
              "invalid_email",
              "method_not_allowed",
              "too_many_requests",
              "db_not_configured",
              "server_error",
              "not_found"
            ]
          },
          "message": {
            "type": "string",
            "description": "What went wrong and how to fix it."
          },
          "docs": {
            "type": "string",
            "format": "uri"
          },
          "fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The request fields to fix."
          }
        }
      },
      "AskRequest": {
        "type": "object",
        "required": [
          "query"
        ],
        "properties": {
          "query": {
            "type": "object",
            "required": [
              "text"
            ],
            "properties": {
              "text": {
                "type": "string",
                "maxLength": 500
              },
              "max_results": {
                "type": "integer",
                "minimum": 1,
                "maximum": 20
              }
            }
          },
          "prefer": {
            "type": "object",
            "properties": {
              "streaming": {
                "type": "boolean"
              },
              "mode": {
                "type": "string",
                "description": "Only list is supported; other modes return the list."
              },
              "response_format": {
                "type": "string"
              }
            }
          },
          "meta": {
            "type": "object",
            "properties": {
              "version": {
                "type": "string",
                "example": "0.55"
              }
            }
          }
        }
      },
      "AskMeta": {
        "type": "object",
        "required": [
          "response_type",
          "version"
        ],
        "properties": {
          "response_type": {
            "type": "string",
            "enum": [
              "answer",
              "failure"
            ]
          },
          "response_format": {
            "type": "string"
          },
          "version": {
            "type": "string"
          }
        }
      },
      "AskItem": {
        "type": "object",
        "required": [
          "@type",
          "url",
          "name"
        ],
        "properties": {
          "@context": {
            "type": "string"
          },
          "@type": {
            "type": "string"
          },
          "@id": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "name": {
            "type": "string"
          },
          "headline": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "inLanguage": {
            "type": "string"
          },
          "datePublished": {
            "type": "string"
          },
          "score": {
            "type": "number",
            "description": "Relevance, 1 for the best match."
          },
          "snippet": {
            "type": "string",
            "description": "The passage that matched."
          },
          "encoding": {
            "type": "object",
            "description": "The same page as markdown: contentUrl."
          }
        }
      },
      "AskResponse": {
        "type": "object",
        "required": [
          "_meta",
          "results"
        ],
        "properties": {
          "_meta": {
            "$ref": "#/components/schemas/AskMeta"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AskItem"
            }
          }
        }
      },
      "AskFailure": {
        "type": "object",
        "required": [
          "_meta",
          "error"
        ],
        "properties": {
          "_meta": {
            "$ref": "#/components/schemas/AskMeta"
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  }
}
