{
  "openapi": "3.1.0",
  "info": {
    "title": "Kumoda API channel",
    "version": "1.0.0",
    "description": "Lets your own system (a website backend, an app, a form) send a customer's message to one of your Kumoda agents and read the conversation. It is the same conversation as on WhatsApp or the website chat window: the same credits, the same handover to a person. Server to server only: browsers cannot call it directly (no CORS access is given). Create the connection in Kumoda under Channels, Add channel, API. The secret is shown once."
  },
  "servers": [{ "url": "https://agents.kumoda.io", "description": "Kumoda agent platform" }],
  "security": [{ "bearer": [] }],
  "paths": {
    "/api/v1/agents/{key}/messages": {
      "parameters": [
        {
          "name": "key",
          "in": "path",
          "required": true,
          "description": "The connection's key, shown in the address in Kumoda: the letter a followed by 24 lowercase hex characters.",
          "schema": { "type": "string", "pattern": "^a[0-9a-f]{24}$", "example": "a0123456789abcdef01234567" }
        }
      ],
      "post": {
        "operationId": "sendMessage",
        "summary": "Send a customer's message and get the reply",
        "description": "The message is saved before this answers, then, unless wait is 0, the call waits up to wait seconds for the reply. Each conversation (one contact) is answered at most 6 times a minute and 150 times a day. Only the first 1,500 characters of text are given to the agent. If a person has taken the conversation over, the agent does not answer and the status is with_a_person.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/SendMessage" },
              "example": { "contact": "customer-123", "text": "Hi, are you open today?", "name": "Ada", "email": "ada@example.com" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The message was accepted. See status.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SendResult" } } }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" }
        }
      },
      "get": {
        "operationId": "readMessages",
        "summary": "Read a contact's conversation",
        "description": "The messages of one contact's conversation after a message id, oldest first, up to 100 at a time. Use the largest id you have seen as after to read only what is new.",
        "parameters": [
          {
            "name": "contact",
            "in": "query",
            "required": true,
            "description": "Your own id for the customer, the same one you send when posting.",
            "schema": { "$ref": "#/components/schemas/Contact" }
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "description": "Only messages with an id above this. Default 0, which reads from the start.",
            "schema": { "type": "integer", "minimum": 0, "default": 0 }
          }
        ],
        "responses": {
          "200": {
            "description": "The messages.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Messages" } } }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "The connection's secret, which starts with ksk_. It is shown once, when the connection is made or the secret is replaced."
      }
    },
    "schemas": {
      "Contact": {
        "type": "string",
        "pattern": "^[A-Za-z0-9._:-]{3,60}$",
        "description": "Your own id for the customer: 3 to 60 letters, numbers, dots, dashes, colons or underscores.",
        "example": "customer-123"
      },
      "SendMessage": {
        "type": "object",
        "required": ["contact", "text"],
        "properties": {
          "contact": { "$ref": "#/components/schemas/Contact" },
          "text": { "type": "string", "maxLength": 4000, "description": "The customer's message. Required. Over 4,000 characters is refused." },
          "name": { "type": "string", "maxLength": 80, "description": "The customer's name. Together with email and phone it makes them a contact in Kumoda." },
          "email": { "type": "string", "maxLength": 120, "description": "The customer's email address. Ignored if it is not a valid address." },
          "phone": { "type": "string", "maxLength": 30, "description": "The customer's phone number. Characters other than digits, +, spaces, brackets and dashes are removed." },
          "messageId": {
            "type": "string",
            "pattern": "^[A-Za-z0-9._:-]{1,64}$",
            "description": "Your id for this message. Sending the same messageId again is not answered twice (the status is duplicate), which makes a retry safe."
          },
          "wait": {
            "type": "number",
            "minimum": 0,
            "maximum": 25,
            "default": 20,
            "description": "Seconds to wait for the reply, up to 25. Use 0 to return at once and read the reply later with GET."
          }
        }
      },
      "SendResult": {
        "type": "object",
        "required": ["status", "reply"],
        "properties": {
          "status": {
            "type": "string",
            "enum": ["replied", "pending", "duplicate", "with_a_person"],
            "description": "replied: the agent (or a person) answered, see reply. pending: no reply yet, read it later with GET. duplicate: this messageId was already sent. with_a_person: a person has taken the conversation, so the agent will not answer."
          },
          "reply": { "type": ["string", "null"], "description": "The reply text, when status is replied." },
          "from": { "type": "string", "enum": ["agent", "human"], "description": "Who wrote the reply. Present when status is replied." },
          "id": { "type": "integer", "description": "The reply's message id. Present when status is replied." }
        }
      },
      "Messages": {
        "type": "object",
        "required": ["messages"],
        "properties": {
          "messages": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["id", "from", "text", "at"],
              "properties": {
                "id": { "type": "integer" },
                "from": { "type": "string", "enum": ["customer", "agent", "human"] },
                "text": { "type": "string" },
                "at": { "type": "string", "format": "date-time" }
              }
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": { "error": { "type": "string", "description": "What went wrong, in words." } }
      }
    },
    "responses": {
      "Error": {
        "description": "400 a field is wrong or missing. 401 the key or the secret is wrong (they are not told apart). 413 the text or the body is too long. 429 more than 120 requests a minute on this connection. 503 the connection is paused.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    }
  }
}
