{
  "openapi": "3.1.0",
  "info": {
    "title": "AIxAI Public API",
    "version": "1.3.0",
    "description": "Public API for AIxAI (https://www.aixai.co.in), an AI agency in Jaipur, India. AIxAI is a consulting/services business, so the API surface is intentionally small: submit a project inquiry, and query the service catalog in natural language.\n\nAuthentication: none. No API keys, tokens, or signup — see https://www.aixai.co.in/auth.md for the full statement of which auth mechanisms are deliberately absent.\n\nMCP: agents can also use the Model Context Protocol servers at https://www.aixai.co.in/api/mcp (product: list_services, get_service, search_services, submit_inquiry) and https://www.aixai.co.in/api/mcp-docs (documentation), both Streamable HTTP and unauthenticated.\n\nSandbox: POST /api/v1/sandbox/send-email is a dedicated sandbox surface that never delivers email, whatever the body says. The live endpoint also accepts \"dryRun\": true (or the header X-Sandbox: true) for the same effect.\n\nPagination: list endpoints use cursor pagination — pass ?limit= and ?cursor=, and read pagination.next_cursor / pagination.next_url from the response.\n\nIdempotency: send an Idempotency-Key header on writes; a repeat within 24 hours replays the first response and sets Idempotent-Replayed: true.\n\nVersioning policy: the API is versioned in the URL path (/api/v1/). Breaking changes only ship in a new version path. When a version or endpoint is scheduled for removal, responses carry Deprecation and Sunset headers at least 90 days before removal, announced at https://www.aixai.co.in/developers.\n\nRate limits: 10 requests/minute per IP on the inquiry endpoint, 30 on /ask, 60 on the MCP servers. Every response carries RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset and RateLimit-Policy; a 429 adds Retry-After.\n\nAgent guidance: https://www.aixai.co.in/llms.txt",
    "contact": {
      "name": "AIxAI",
      "email": "HQ@aixai.co.in",
      "url": "https://www.aixai.co.in/contact"
    }
  },
  "servers": [
    {
      "url": "https://www.aixai.co.in"
    }
  ],
  "paths": {
    "/api/v1/send-email": {
      "post": {
        "operationId": "submitContactInquiry",
        "summary": "Submit a project inquiry to AIxAI",
        "description": "Sends a contact-form inquiry to the AIxAI team. Use this when a user wants to engage AIxAI for AI consulting, automation, chatbot, or related services. Always obtain the user's consent before submitting on their behalf; the AIxAI team replies to the provided email address.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactInquiry"
              },
              "example": {
                "name": "Ada Lovelace",
                "email": "ada@example.com",
                "message": "We want a RAG chatbot over our internal docs. Can you help?"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Inquiry sent successfully.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed per minute.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining 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/SuccessResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation failed (missing or invalid fields).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed (only POST is accepted).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (10 requests/minute per IP). Carries a Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per minute.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining 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/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "The inquiry could not be delivered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Client-generated unique key (e.g. a UUID). A repeated request with the same key within 24 hours replays the original response instead of sending a second email.",
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "X-Sandbox",
            "in": "header",
            "required": false,
            "description": "Set to \"true\" to validate the request without delivering email (same as dryRun in the body).",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            }
          }
        ]
      }
    },
    "/api/send-email": {
      "post": {
        "operationId": "submitContactInquiryUnversioned",
        "summary": "Submit a project inquiry (unversioned alias)",
        "description": "Alias of POST /api/v1/send-email kept for backwards compatibility. Prefer the versioned path; if this alias is ever scheduled for removal it will emit Deprecation and Sunset headers at least 90 days ahead.",
        "deprecated": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactInquiry"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Inquiry sent successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "The inquiry could not be delivered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Client-generated unique key (e.g. a UUID). A repeated request with the same key within 24 hours replays the original response instead of sending a second email.",
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "X-Sandbox",
            "in": "header",
            "required": false,
            "description": "Set to \"true\" to validate the request without delivering email (same as dryRun in the body).",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            }
          }
        ]
      }
    },
    "/ask": {
      "get": {
        "operationId": "askNaturalLanguageQuery",
        "summary": "Ask which AIxAI services match a need (NLWeb)",
        "description": "NLWeb-compatible natural-language query over the AIxAI service catalog. Returns ranked matches with schema.org Service objects. Request Server-Sent Events with Accept: text/event-stream, Prefer: streaming=true, or streaming=true.",
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": true,
            "description": "What the user needs, in plain language",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "streaming",
            "in": "query",
            "required": false,
            "description": "Set true for a Server-Sent Events stream",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Ranked matches. Server-Sent Events (start/result/complete) when streaming is requested.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AskResponse"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "SSE stream: event: start, event: result (one per match), event: complete"
                }
              }
            }
          },
          "400": {
            "description": "Missing query.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AskResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (30/minute per IP).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AskResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "askNaturalLanguageQueryPost",
        "summary": "Ask which AIxAI services match a need (NLWeb, POST)",
        "description": "Same as GET /ask. The body accepts either {\"query\": \"...\"} or the NLWeb {\"query\": {\"text\": \"...\"}} form.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "query"
                ],
                "properties": {
                  "query": {
                    "oneOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "object",
                        "properties": {
                          "text": {
                            "type": "string"
                          }
                        }
                      }
                    ],
                    "description": "The natural-language query"
                  },
                  "streaming": {
                    "type": "boolean",
                    "description": "Request a Server-Sent Events stream"
                  }
                }
              },
              "example": {
                "query": "we need a chatbot over our internal documents",
                "streaming": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ranked matches. Server-Sent Events (start/result/complete) when streaming is requested.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AskResponse"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "SSE stream: event: start, event: result (one per match), event: complete"
                }
              }
            }
          },
          "400": {
            "description": "Missing query.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AskResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (30/minute per IP).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AskResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/services": {
      "get": {
        "operationId": "listServices",
        "summary": "List the AIxAI service catalog",
        "description": "Returns the AIxAI services with cursor pagination. Pass ?limit= for page size and ?cursor= (the slug of the last item on the previous page) to continue. The response's pagination.next_cursor and pagination.next_url carry the continuation; both are null on the final page. No authentication required.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size, 1-100 (default 10).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Slug of the last item on the previous page.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of services.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServicePage"
                }
              }
            }
          },
          "400": {
            "description": "Invalid limit or unknown cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sandbox/send-email": {
      "post": {
        "operationId": "submitContactInquirySandbox",
        "summary": "Submit a project inquiry (sandbox — never delivers)",
        "description": "Sandbox twin of POST /api/v1/send-email with an identical request and response contract, except that it never delivers email regardless of the body. Use it to rehearse a call, verify your request shape, and exercise the error and rate-limit behaviour without touching production. Responses carry X-Sandbox: true and \"sandbox\": true.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactInquiry"
              },
              "example": {
                "name": "Ada Lovelace",
                "email": "ada@example.com",
                "message": "We want a RAG chatbot over our internal docs. Can you help?"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Inquiry sent successfully.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed per minute.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining 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/SuccessResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation failed (missing or invalid fields).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed (only POST is accepted).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (10 requests/minute per IP). Carries a Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per minute.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining 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/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "The inquiry could not be delivered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Client-generated unique key (e.g. a UUID). A repeated request with the same key within 24 hours replays the original response instead of sending a second email.",
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "X-Sandbox",
            "in": "header",
            "required": false,
            "description": "Set to \"true\" to validate the request without delivering email (same as dryRun in the body).",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            }
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "ContactInquiry": {
        "type": "object",
        "required": [
          "name",
          "email",
          "message"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 200,
            "description": "Name of the person or company making the inquiry."
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 320,
            "description": "Reply-to email address for the inquiry."
          },
          "message": {
            "type": "string",
            "maxLength": 5000,
            "description": "The inquiry itself: what the user wants to build or discuss."
          },
          "dryRun": {
            "type": "boolean",
            "default": false,
            "description": "Validate the request without delivering the email. Use this to rehearse a call safely."
          }
        }
      },
      "SuccessResponse": {
        "type": "object",
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "type": "string",
            "example": "Email sent successfully!"
          },
          "dryRun": {
            "type": "boolean",
            "description": "Present and true when the request was validated but not delivered."
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "message": {
            "type": "string",
            "description": "Human-readable error summary (kept for backwards compatibility)."
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "method_not_allowed",
                  "validation_error",
                  "rate_limited",
                  "not_found",
                  "delivery_failed"
                ],
                "description": "Machine-readable error code."
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation of the error."
              },
              "hint": {
                "type": "string",
                "description": "How to resolve the error."
              }
            }
          }
        }
      },
      "NLWebMeta": {
        "type": "object",
        "required": [
          "response_type",
          "version"
        ],
        "properties": {
          "response_type": {
            "type": "string",
            "enum": [
              "results",
              "answer",
              "failure",
              "start",
              "result",
              "complete"
            ]
          },
          "version": {
            "type": "string",
            "example": "0.55"
          }
        }
      },
      "AskResponse": {
        "type": "object",
        "required": [
          "_meta"
        ],
        "properties": {
          "_meta": {
            "$ref": "#/components/schemas/NLWebMeta"
          },
          "query": {
            "type": "string"
          },
          "query_id": {
            "type": "string"
          },
          "results": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "url": {
                  "type": "string",
                  "description": "Canonical URL of the matching service"
                },
                "name": {
                  "type": "string"
                },
                "site": {
                  "type": "string"
                },
                "score": {
                  "type": "number",
                  "description": "Relevance score; higher is better"
                },
                "description": {
                  "type": "string"
                },
                "schema_object": {
                  "type": "object",
                  "description": "schema.org Service object for the match"
                }
              }
            }
          }
        }
      },
      "Service": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "description": "Stable identifier, also the pagination cursor"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "longDescription": {
            "type": "string"
          },
          "benefits": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "useCases": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "url": {
            "type": "string"
          }
        }
      },
      "ServicePage": {
        "type": "object",
        "required": [
          "data",
          "pagination"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Service"
            }
          },
          "pagination": {
            "type": "object",
            "required": [
              "limit",
              "has_more",
              "next_cursor"
            ],
            "properties": {
              "limit": {
                "type": "integer",
                "description": "Page size used for this response"
              },
              "has_more": {
                "type": "boolean",
                "description": "True when further pages exist"
              },
              "next_cursor": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Pass as ?cursor= to fetch the next page; null on the last page"
              },
              "next_url": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Fully-formed URL for the next page, or null"
              },
              "total": {
                "type": "integer",
                "description": "Total number of services"
              }
            }
          }
        }
      }
    }
  }
}
