{
  "openapi": "3.0.4",
  "info": {
    "title": "meiti API",
    "description": "**Beta.** v1 is ready to build on, but small details may still change before we version it for go-live. Every change is listed in the [changelog](https://api.meiti.ai/v1/changelog) — last change: 2026-10-11.\n\n**Authentication.** Create a service key in the meiti app (inbox settings → API access) and send it as `X-Api-Key: meiti_sk_…` or `Authorization: Bearer meiti_sk_…`. A key acts on exactly one inbox.\n\n**Errors.** Every error is an RFC 9457 problem details object (`application/problem+json`): a stable code in `title`, a sentence in `detail`. Rate limits answer 429 with `Retry-After`.\n\n**Contact.** Questions, feedback or something missing? Write to [support@meiti.ai](mailto:support@meiti.ai).",
    "contact": {
      "name": "meiti",
      "email": "support@meiti.ai"
    },
    "version": "v1 (Beta)"
  },
  "servers": [
    {
      "url": "https://api.meiti.ai"
    }
  ],
  "paths": {
    "/v1/agents": {
      "get": {
        "tags": [
          "Agents"
        ],
        "summary": "The inbox's own-AI assistants, each with its tool skills (`skills`: key, name, kind, …).",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ExternalAgentDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/agents/{id}": {
      "get": {
        "tags": [
          "Agents"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalAgentDto"
                }
              }
            }
          },
          "404": {
            "description": "Not found in this inbox (e.g. contact_not_found).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Agents"
        ],
        "summary": "Point the agent at your endpoints. Omitted fields stay as they are; an empty string disconnects that channel (`chatUrl`, `voiceUrl`) or returns to meiti's default voice (`voiceId`). `chatUrl` must be https, `voiceUrl` wss; both must resolve to a public address.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateExternalAgentRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateExternalAgentRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateExternalAgentRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalAgentDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request; the problem's title names the field rule (e.g. invalid_phone).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not found in this inbox (e.g. contact_not_found).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/agents/{id}/secret": {
      "post": {
        "tags": [
          "Agents"
        ],
        "summary": "Creates a new signing secret and returns it — the only time it can be read. The previous one stops working at once. meiti signs every request with it: `X-Meiti-Signature: t=…,v1=hex(HMAC-SHA256(secret, t + \".\" + body))`; on the phone WebSocket the signed text is the `X-Meiti-Call-Id` header instead of a body.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalAgentSecretResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found in this inbox (e.g. contact_not_found).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/agents/{id}/test": {
      "post": {
        "tags": [
          "Agents"
        ],
        "summary": "Sends a signed `ping` to the chat URL and opens (and closes) the voice WebSocket.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalAgentTestResult"
                }
              }
            }
          },
          "404": {
            "description": "Not found in this inbox (e.g. contact_not_found).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/projects/{id}/agent-replies": {
      "post": {
        "tags": [
          "Agents"
        ],
        "summary": "Answers a conversation as its connected AI — the late answer to a `message.received` your endpoint acknowledged with 202. The text reaches the customer on the channel they wrote on; `action: handover` passes the conversation to the team (the following `message.received` carry `autoReply.state: off` until a member hands it back). The 202 carries the conversation's `autoReply`: when the team has taken over in the meantime the text is delivered anyway (the team sees a note), skills are not run and `handover` is a no-op. 409 `project_not_with_agent` when the project is not answered by an own-AI assistant.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExternalAgentReply"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/ExternalAgentReply"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/ExternalAgentReply"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalAgentReplyAccepted"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request; the problem's title names the field rule (e.g. invalid_phone).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not found in this inbox (e.g. contact_not_found).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflicts with the current state (e.g. appointment_closed, idempotency_in_progress).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable Content",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/projects/{id}/skills/{key}": {
      "post": {
        "tags": [
          "Agents"
        ],
        "summary": "Runs one tool skill of the conversation's own-AI assistant now — by its `key` (or id), as listed in `message.received` / `GET v1/agents`. Chat semantics: a forward hands the conversation to the team (as a chat handover; a live call is transferred with the phone frame `skill` instead), an alert notifies the team with `note` as its text. meiti's rules apply (time window, limits, plan): a skill that may not run answers `200` with `ok: false` and a `reason` (`unknown-skill`, `outside-hours`, `limit-reached`, `not-allowed`, `no-target`, `no-recipient`, `failed`). Each executed skill costs like any skill run. With an `Idempotency-Key` header a retry within 24 hours replays the first answer instead of running again.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExternalAgentSkillRunRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/ExternalAgentSkillRunRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/ExternalAgentSkillRunRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalAgentSkillResult"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request; the problem's title names the field rule (e.g. invalid_phone).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not found in this inbox (e.g. contact_not_found).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflicts with the current state (e.g. appointment_closed, idempotency_in_progress).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable Content",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/appointments": {
      "post": {
        "tags": [
          "Appointments"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicCreateAppointmentRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicCreateAppointmentRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PublicCreateAppointmentRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicAppointmentDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request; the problem's title names the field rule (e.g. invalid_phone).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not found in this inbox (e.g. contact_not_found).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable Content",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Appointments"
        ],
        "summary": "Ascending by last change — poll with the newest updatedAt you have seen.",
        "parameters": [
          {
            "name": "updatedSince",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "externalId",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "contactId",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "take",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PublicAppointmentDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/appointments/{id}": {
      "get": {
        "tags": [
          "Appointments"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicAppointmentDto"
                }
              }
            }
          },
          "404": {
            "description": "Not found in this inbox (e.g. contact_not_found).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Appointments"
        ],
        "summary": "Move, relabel or mark as agreed. A new start or duration re-invites the customer unless confirmed=true.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicUpdateAppointmentRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicUpdateAppointmentRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PublicUpdateAppointmentRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicAppointmentDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request; the problem's title names the field rule (e.g. invalid_phone).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not found in this inbox (e.g. contact_not_found).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflicts with the current state (e.g. appointment_closed, idempotency_in_progress).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Appointments"
        ],
        "summary": "Business-side cancellation. meiti tells the customer; an optional message follows the notice.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicCancelAppointmentRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicCancelAppointmentRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PublicCancelAppointmentRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicAppointmentDto"
                }
              }
            }
          },
          "404": {
            "description": "Not found in this inbox (e.g. contact_not_found).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflicts with the current state (e.g. appointment_closed, idempotency_in_progress).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/contacts/{id}": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicContactDto"
                }
              }
            }
          },
          "404": {
            "description": "Not found in this inbox (e.g. contact_not_found).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/contacts": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "summary": "Look a contact up by phone or email. 404 when the address is unknown to this inbox.",
        "parameters": [
          {
            "name": "phone",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "email",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicContactDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request; the problem's title names the field rule (e.g. invalid_phone).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not found in this inbox (e.g. contact_not_found).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Contacts"
        ],
        "summary": "Create (201) or update (200) the contact that owns the phone number.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicUpsertContactRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicUpsertContactRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PublicUpsertContactRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicContactDto"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicContactDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request; the problem's title names the field rule (e.g. invalid_phone).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable Content",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/me": {
      "get": {
        "tags": [
          "Key"
        ],
        "summary": "Which inbox this key acts on. The cheapest way to check a key works.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicWorkspaceInfo"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messages/log": {
      "post": {
        "tags": [
          "Log"
        ],
        "summary": "File a message that was sent or received outside meiti — an email from your own mailbox, a WhatsApp or SMS from another tool — into the contact's project. Nothing is sent and meiti's assistant does not answer it; it reads it as part of the conversation. An incoming message shows as unread for the team. 201 when filed; 200 when the same `externalId` was logged before on that channel (nothing is filed again).",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicLogMessageRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicLogMessageRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PublicLogMessageRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicLogMessageResponse"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicLogMessageResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request; the problem's title names the field rule (e.g. invalid_phone).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not found in this inbox (e.g. contact_not_found).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable Content",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/notes": {
      "post": {
        "tags": [
          "Log"
        ],
        "summary": "Add an internal note to a project — for your team only: never sent to the customer, never read by meiti's assistant. With an `Idempotency-Key` header a retry within 24 hours replays the first answer instead of adding the note again.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicAddNoteRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicAddNoteRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PublicAddNoteRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicAddNoteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request; the problem's title names the field rule (e.g. invalid_phone).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not found in this inbox (e.g. contact_not_found).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflicts with the current state (e.g. appointment_closed, idempotency_in_progress).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable Content",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messages": {
      "post": {
        "tags": [
          "Messages"
        ],
        "summary": "Send one text message to one contact of the key's inbox. With an `Idempotency-Key` header a retry of the same request within 24 hours replays the first answer instead of sending again.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicSendMessageRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicSendMessageRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PublicSendMessageRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicSendMessageResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request; the problem's title names the field rule (e.g. invalid_phone).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Refused by a consent rule (e.g. marketing_consent_missing).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not found in this inbox (e.g. contact_not_found).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflicts with the current state (e.g. appointment_closed, idempotency_in_progress).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable Content",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/projects": {
      "get": {
        "tags": [
          "Projects"
        ],
        "summary": "Newest first; optionally only one contact's projects.",
        "parameters": [
          {
            "name": "contactId",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "take",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ConversationSummaryDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/projects/{id}/messages": {
      "get": {
        "tags": [
          "Projects"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConversationTranscriptDto"
                }
              }
            }
          },
          "404": {
            "description": "Not found in this inbox (e.g. contact_not_found).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/senders": {
      "get": {
        "tags": [
          "Senders"
        ],
        "summary": "The inbox's active senders. Put an `id` into `channels` to send from exactly that sender — useful when the inbox has several mailboxes; a channel name (`email`) uses the default one.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PublicSenderDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/whatsapp/templates": {
      "get": {
        "tags": [
          "Senders"
        ],
        "summary": "The approved WhatsApp templates of the inbox's own number (German and English), with how many body variables each needs. Read live from WhatsApp.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PublicWhatsAppTemplateDto"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable Content",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WebhookSubscriptionDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWebhookRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWebhookRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWebhookRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateWebhookResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request; the problem's title names the field rule (e.g. invalid_phone).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable Content",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks/events": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Supported event names, so a client can discover them without reading docs.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks/{id}": {
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not found in this inbox (e.g. contact_not_found).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks/{id}/test": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Queues a \"ping\" event to the subscription. Arrives within about a minute.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted"
          },
          "404": {
            "description": "Not found in this inbox (e.g. contact_not_found).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached; see Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ConversationMessageDto": {
        "type": "object",
        "properties": {
          "sender": {
            "type": "string",
            "description": "customer / assistant / employee / system",
            "nullable": true
          },
          "text": {
            "type": "string",
            "nullable": true
          },
          "messageType": {
            "type": "string",
            "nullable": true
          },
          "channel": {
            "$ref": "#/components/schemas/MessengerChannel"
          },
          "sentAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "ConversationSummaryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int32"
          },
          "contactId": {
            "type": "integer",
            "description": "The inbox's contact behind this conversation; null when none is linked.",
            "format": "int32",
            "nullable": true
          },
          "customerName": {
            "type": "string",
            "nullable": true
          },
          "customerPhone": {
            "type": "string",
            "nullable": true
          },
          "channel": {
            "$ref": "#/components/schemas/MessengerChannel"
          },
          "status": {
            "type": "string",
            "nullable": true
          },
          "lastMessageAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "messageCount": {
            "type": "integer",
            "format": "int32"
          },
          "lastMessagePreview": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Read-only conversation surface for the setup MCP: lets a guided-setup session SEE what actually happened in live test calls/chats and discuss it with the user — the interactive-setup loop."
      },
      "ConversationTranscriptDto": {
        "type": "object",
        "properties": {
          "projectId": {
            "type": "integer",
            "format": "int32"
          },
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConversationMessageDto"
            },
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "CreateWebhookRequest": {
        "required": [
          "url"
        ],
        "type": "object",
        "properties": {
          "url": {
            "maxLength": 2000,
            "minLength": 1,
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Event names from GET v1/webhooks/events. Empty = all of them.",
            "nullable": true
          },
          "description": {
            "maxLength": 200,
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "CreateWebhookResponse": {
        "type": "object",
        "properties": {
          "subscription": {
            "$ref": "#/components/schemas/WebhookSubscriptionDto"
          },
          "secret": {
            "type": "string",
            "description": "HMAC secret for the X-Meiti-Signature header. Shown once.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "The only response that carries the signing secret."
      },
      "ExternalAgentAutoReply": {
        "type": "object",
        "properties": {
          "state": {
            "type": "string",
            "description": "`on` (the AI is expected to answer), `paused` (muted until `pausedUntil`) or `off` (the team answers until a member hands the conversation back to the AI).",
            "nullable": true
          },
          "pausedUntil": {
            "type": "string",
            "description": "When a pause ends on its own (state `paused`); null otherwise.",
            "format": "date-time",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A conversation's auto-reply (\"Auto-Antwort\" in the app): whether the team wants the AI to answer it — the same switch meiti's own assistant obeys. A member taking over or replying turns it off, \"An meiti übergeben\" turns it back on. meiti does not enforce it on the connected AI: a reply while it is not `on` is delivered."
      },
      "ExternalAgentDto": {
        "type": "object",
        "properties": {
          "agentId": {
            "type": "integer",
            "description": "The meiti assistant id (BotProfile).",
            "format": "int32"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "chatUrl": {
            "type": "string",
            "description": "https webhook for chat; null = chat not connected.",
            "nullable": true
          },
          "voiceUrl": {
            "type": "string",
            "description": "wss endpoint for phone calls; null = phone not connected.",
            "nullable": true
          },
          "timeoutSeconds": {
            "type": "integer",
            "format": "int32"
          },
          "hasSecret": {
            "type": "boolean"
          },
          "voiceId": {
            "type": "string",
            "description": "The ElevenLabs voice id the phone relay speaks with (null = meiti's default voice).",
            "nullable": true
          },
          "lastSuccessAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "lastFailureAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "lastError": {
            "type": "string",
            "nullable": true
          },
          "consecutiveFailures": {
            "type": "integer",
            "format": "int32"
          },
          "skills": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ExternalAgentToolSkillDto"
            },
            "description": "The tool skills the agent can run (Forward / Alert), in the assistant's order.",
            "nullable": true
          },
          "skillsPerHourLimit": {
            "type": "integer",
            "description": "Skill runs per conversation and hour (the assistant's limit, every skill counts).",
            "format": "int32"
          },
          "forwardsPerCallLimit": {
            "type": "integer",
            "description": "Forwards per phone call (the assistant's per-call limit).",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "An assistant connected as an external agent, as the app and `v1/agents` show it."
      },
      "ExternalAgentReply": {
        "type": "object",
        "properties": {
          "text": {
            "maxLength": 4000,
            "type": "string",
            "nullable": true
          },
          "action": {
            "maxLength": 20,
            "type": "string",
            "description": "`none` (default), `handover` or `end` — see `externalAgentProtocol`.Actions.",
            "nullable": true
          },
          "note": {
            "maxLength": 500,
            "type": "string",
            "description": "Optional note for the team, shown in the conversation (not sent to the customer). Used with `handover`; also the alert text of an alert in `externalAgentReply`.ExecuteSkills.",
            "nullable": true
          },
          "executeSkills": {
            "maxItems": 5,
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tool skills to run, by key (or id as a string), at most `externalAgentReplyLimits`.MaxExecuteSkills. The text goes out first; alerts run before a forward; only one forward per reply.",
            "nullable": true
          },
          "announcement": {
            "maxLength": 200,
            "type": "string",
            "description": "Overrides the configured announcement of the skills in this reply; `\"\"` suppresses it. Null = the configured one.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "The agent's answer to a `message.received`: in the 200 body of the webhook, or later as the body of `POST v1/projects/{id}/agent-replies`. Empty text with action `none` = no reply."
      },
      "ExternalAgentReplyAccepted": {
        "type": "object",
        "properties": {
          "projectId": {
            "type": "integer",
            "format": "int32"
          },
          "messageId": {
            "type": "integer",
            "description": "The stored reply message; null when the reply only carried an action.",
            "format": "int32",
            "nullable": true
          },
          "status": {
            "type": "string",
            "description": "`sent`, `handed_over` or `noop`.",
            "nullable": true
          },
          "skills": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ExternalAgentSkillResult"
            },
            "description": "One result per entry of `executeSkills`; null when the reply named none.",
            "nullable": true
          },
          "autoReply": {
            "$ref": "#/components/schemas/ExternalAgentAutoReply"
          }
        },
        "additionalProperties": false,
        "description": "202 answer of `POST v1/projects/{id}/agent-replies`."
      },
      "ExternalAgentSecretResponse": {
        "type": "object",
        "properties": {
          "secret": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "The signing secret — returned exactly once, by create-secret / rotate."
      },
      "ExternalAgentSkillResult": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "The key (or id) exactly as the agent sent it.",
            "nullable": true
          },
          "ok": {
            "type": "boolean"
          },
          "reason": {
            "type": "string",
            "description": "Null on a plain success; otherwise one of `externalAgentProtocol`.SkillReasons.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "The outcome of one requested skill."
      },
      "ExternalAgentSkillRunRequest": {
        "type": "object",
        "properties": {
          "note": {
            "maxLength": 500,
            "type": "string",
            "description": "Alert: the alert text (default: a line naming the conversation and contact). Forward: a note for the team.",
            "nullable": true
          },
          "announcement": {
            "maxLength": 200,
            "type": "string",
            "description": "Overrides the skill's configured announcement for this run; `\"\"` = say nothing.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Optional body of `POST v1/projects/{id}/skills/{key}`."
      },
      "ExternalAgentTestResult": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "statusCode": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "durationMs": {
            "type": "integer",
            "format": "int32"
          },
          "error": {
            "type": "string",
            "description": "Why it failed, in plain words (no response body).",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Result of \"Verbindung testen\": a signed `ping` to the chat URL, or a connect to the voice URL."
      },
      "ExternalAgentToolSkillDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "The skill's id; `\"12\"` works in `executeSkills` too.",
            "format": "int32"
          },
          "key": {
            "type": "string",
            "description": "The stable key to name the skill by (the skill's slug).",
            "nullable": true
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "kind": {
            "type": "string",
            "description": "`forward` or `alert` — see `externalAgentProtocol`.Skills.",
            "nullable": true
          },
          "announcement": {
            "type": "string",
            "description": "What meiti says to the customer when the skill runs (configured on the assistant page); null = nothing. The agent does not need to say it itself.",
            "nullable": true
          },
          "hoursLimited": {
            "type": "boolean",
            "description": "The skill only runs inside its own time window (\"Zeiten\")."
          }
        },
        "additionalProperties": false,
        "description": "A tool skill on the assistant page and in `v1/agents`: the event shape plus its configuration summary."
      },
      "MessengerChannel": {
        "enum": [
          1,
          2,
          3,
          4,
          5,
          6,
          7,
          8,
          9,
          10,
          11,
          12
        ],
        "type": "integer",
        "format": "int32"
      },
      "ProblemDetails": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "nullable": true
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "detail": {
            "type": "string",
            "nullable": true
          },
          "instance": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": { }
      },
      "PublicAddNoteRequest": {
        "required": [
          "text"
        ],
        "type": "object",
        "properties": {
          "projectId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "contactId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "phone": {
            "maxLength": 40,
            "type": "string",
            "nullable": true
          },
          "email": {
            "maxLength": 320,
            "type": "string",
            "nullable": true
          },
          "title": {
            "maxLength": 80,
            "type": "string",
            "nullable": true
          },
          "text": {
            "minLength": 1,
            "type": "string"
          }
        },
        "additionalProperties": false,
        "description": "An internal note on a project — for the team only: never sent to the customer and never read by meiti's assistant. Exactly one of `projectId`, `contactId`, `phone` or `email` says where it belongs; a contact address files it on the contact's current project (or opens one)."
      },
      "PublicAddNoteResponse": {
        "type": "object",
        "properties": {
          "projectId": {
            "type": "integer",
            "format": "int32"
          },
          "contactId": {
            "type": "integer",
            "format": "int32"
          },
          "noteId": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      },
      "PublicAppointmentDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int32"
          },
          "externalId": {
            "type": "string",
            "description": "The caller's own id for this appointment, echoed on every read and webhook.",
            "nullable": true
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "nullable": true
            },
            "nullable": true
          },
          "contactId": {
            "type": "integer",
            "format": "int32"
          },
          "projectId": {
            "type": "integer",
            "format": "int32"
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "start": {
            "type": "string",
            "description": "Start in UTC. Null while the appointment is still a multi-slot proposal.",
            "format": "date-time",
            "nullable": true
          },
          "startLocal": {
            "type": "string",
            "description": "Start in the inbox's local time, as the customer sees it.",
            "format": "date-time",
            "nullable": true
          },
          "durationMinutes": {
            "type": "integer",
            "format": "int32"
          },
          "kind": {
            "type": "string",
            "description": "\"in_person\", \"phone\" or \"video\".",
            "nullable": true
          },
          "status": {
            "type": "string",
            "nullable": true
          },
          "customerConfirmedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "businessConfirmedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "cancelledAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "finishedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "rescheduledCount": {
            "type": "integer",
            "format": "int32"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "PublicCancelAppointmentRequest": {
        "type": "object",
        "properties": {
          "message": {
            "maxLength": 1000,
            "type": "string",
            "description": "Optional text meiti sends the customer after the cancellation notice.",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PublicContactDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int32"
          },
          "salutation": {
            "type": "string",
            "nullable": true
          },
          "firstName": {
            "type": "string",
            "nullable": true
          },
          "lastName": {
            "type": "string",
            "nullable": true
          },
          "company": {
            "type": "string",
            "nullable": true
          },
          "phone": {
            "type": "string",
            "description": "E.164. The number meiti reaches the contact on; null for contacts known by email only.",
            "nullable": true
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "serviceConsent": {
            "type": "string",
            "description": "Service consent on the contact's phone number in this inbox: `unknown`, `granted` or `denied`. \"unknown\" means never asked, not \"no\".",
            "nullable": true
          },
          "serviceConsentAt": {
            "type": "string",
            "description": "When `serviceConsent` was last set to granted or denied; null while unknown.",
            "format": "date-time",
            "nullable": true
          },
          "marketingConsent": {
            "type": "string",
            "description": "Separate marketing consent on the phone number — required for `purpose: \"marketing\"` sends.",
            "nullable": true
          },
          "marketingConsentAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "PublicCreateAppointmentRequest": {
        "required": [
          "start"
        ],
        "type": "object",
        "properties": {
          "contactId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "phone": {
            "maxLength": 40,
            "type": "string",
            "nullable": true
          },
          "email": {
            "maxLength": 320,
            "type": "string",
            "nullable": true
          },
          "start": {
            "type": "string",
            "format": "date-time"
          },
          "durationMinutes": {
            "maximum": 1440,
            "minimum": 5,
            "type": "integer",
            "format": "int32"
          },
          "title": {
            "maxLength": 120,
            "type": "string",
            "nullable": true
          },
          "kind": {
            "maxLength": 20,
            "type": "string",
            "description": "\"in_person\" (default), \"phone\" or \"video\".",
            "nullable": true
          },
          "bookingTypeId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "confirmed": {
            "type": "boolean"
          },
          "externalId": {
            "maxLength": 100,
            "type": "string",
            "nullable": true
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "nullable": true
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Create an appointment the caller's system already holds. Recipient like the send endpoint. `confirmed` = the customer already agreed elsewhere: meiti sends a confirmation and reminders. Otherwise meiti sends an invitation and collects the customer's confirmation. Availability is NOT checked — the caller's calendar is authoritative for its own appointments."
      },
      "PublicLogMessageRequest": {
        "required": [
          "channel",
          "direction",
          "text"
        ],
        "type": "object",
        "properties": {
          "projectId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "contactId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "phone": {
            "maxLength": 40,
            "type": "string",
            "nullable": true
          },
          "email": {
            "maxLength": 320,
            "type": "string",
            "nullable": true
          },
          "direction": {
            "minLength": 1,
            "type": "string",
            "description": "`in` = the customer wrote it, `out` = the business wrote it."
          },
          "channel": {
            "minLength": 1,
            "type": "string",
            "description": "`email`, `whatsapp`, `sms` or `rcs` — where the message was actually exchanged."
          },
          "subject": {
            "maxLength": 500,
            "type": "string",
            "description": "Email only: the subject line; it is shown above the text.",
            "nullable": true
          },
          "text": {
            "minLength": 1,
            "type": "string",
            "description": "The message text. Longer than 5000 characters (subject included) is cut, see `truncated`."
          },
          "occurredAt": {
            "type": "string",
            "description": "When the message was written (UTC). Omitted = now. Not more than 5 minutes in the future.",
            "format": "date-time",
            "nullable": true
          },
          "externalId": {
            "maxLength": 200,
            "type": "string",
            "description": "Your id of the message (e.g. the email's Message-ID). Logging the same id on the same channel again files nothing and answers with the message logged first.",
            "nullable": true
          },
          "ai": {
            "type": "string",
            "description": "`pause` = your team handles this project, meiti's assistant stays quiet like after a team member's reply; `resume` = hand the project back to the assistant. Omitted = the assistant's state is unchanged.",
            "nullable": true
          },
          "notify": {
            "type": "boolean",
            "description": "Incoming messages only: also send the team the push notification of a new customer message. Omitted = false; a logged incoming message is always marked unread.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "File a message that was sent or received OUTSIDE meiti (an email from the business's own mailbox, a WhatsApp or SMS from another tool) into a contact's project. Nothing is sent, and meiti's assistant never answers a logged message. Exactly one of `projectId`, `contactId`, `phone` or `email` says where it belongs; a contact address files it on the contact's current project (or opens one)."
      },
      "PublicLogMessageResponse": {
        "type": "object",
        "properties": {
          "projectId": {
            "type": "integer",
            "format": "int32"
          },
          "contactId": {
            "type": "integer",
            "format": "int32"
          },
          "messageId": {
            "type": "integer",
            "format": "int32"
          },
          "created": {
            "type": "boolean",
            "description": "False when `externalId` was logged before; the ids are then those of the first log."
          },
          "truncated": {
            "type": "boolean",
            "description": "The text was longer than 5000 characters and was cut."
          }
        },
        "additionalProperties": false
      },
      "PublicSendMessageRequest": {
        "required": [
          "text"
        ],
        "type": "object",
        "properties": {
          "contactId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "phone": {
            "maxLength": 40,
            "type": "string",
            "description": "Any readable phone format; normalised to E.164 on the server.",
            "nullable": true
          },
          "email": {
            "maxLength": 320,
            "type": "string",
            "nullable": true
          },
          "text": {
            "maxLength": 5000,
            "minLength": 1,
            "type": "string"
          },
          "purpose": {
            "maxLength": 20,
            "type": "string",
            "description": "Optional delivery type: `none` (default, also when omitted) always sends, `service` is refused when the recipient declined service messages on the addressed address, `marketing` requires granted marketing consent there. Not part of the idempotency comparison while omitted.",
            "nullable": true
          },
          "channel": {
            "maxLength": 20,
            "type": "string",
            "description": "Optional single channel: `whatsapp`, `sms`, `rcs` or `email`; omitted = the contact's default channel. Shorthand for `channels: [channel, \"default\"]` (`[channel]` with `allowFallback: false`). Not part of the idempotency comparison while omitted.",
            "nullable": true
          },
          "allowFallback": {
            "type": "boolean",
            "description": "Whether meiti may use another channel when the requested one cannot reach the contact: the contact's default channel at send time, and the automatic SMS rescue after a failed chat-channel delivery. Omitted = true (the behaviour without a channel). Not part of the idempotency comparison while omitted.",
            "nullable": true
          },
          "channels": {
            "maxItems": 10,
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Optional channels in priority order. Each entry is a channel (`whatsapp`, `sms`, `rcs`, `email`), a sender id from GET v1/senders (e.g. `email_42`), or `default` (the contact's default channel). meiti uses the first entry that can reach the contact; delivery never leaves the list. Omitted = the contact's default channel. Cannot be combined with `channel`.",
            "nullable": true
          },
          "respectContactSettings": {
            "type": "boolean",
            "description": "Whether the contact's settings decide (default `true`): a channel whose address declined service messages (for `purpose: \"service\"`) or lacks marketing consent (for `purpose: \"marketing\"`) is skipped. `false` sends an important service message even to a contact who declined service messages; marketing can never be enforced.",
            "nullable": true
          },
          "whatsApp": {
            "$ref": "#/components/schemas/PublicWhatsAppTemplateRequest"
          }
        },
        "additionalProperties": false,
        "description": "Send one text message from the key's inbox to one contact. Exactly one of `contactId`, `phone` or `email` addresses the recipient; the server resolves the contact's open project or opens one."
      },
      "PublicSendMessageResponse": {
        "type": "object",
        "properties": {
          "projectId": {
            "type": "integer",
            "format": "int32"
          },
          "contactId": {
            "type": "integer",
            "format": "int32"
          },
          "messageId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "status": {
            "type": "string",
            "description": "\"queued\" (delivery runs in the background) or \"sent\".",
            "nullable": true
          },
          "channel": {
            "type": "string",
            "description": "The channel the message goes out on: `whatsapp`, `sms`, `rcs`, `email` or `webchat`.",
            "nullable": true
          },
          "senderId": {
            "type": "string",
            "description": "The sender used, as listed by GET v1/senders; null when it is not one of them.",
            "nullable": true
          },
          "sentAsTemplate": {
            "type": "boolean",
            "description": "The WhatsApp template was sent instead of the text (closed 24-hour window)."
          },
          "waitingForReply": {
            "type": "boolean",
            "description": "WhatsApp's 24-hour window is closed and no template was given: the customer receives a notice and the text is delivered when they answer."
          },
          "consentOverridden": {
            "type": "boolean",
            "description": "The send went out although the contact declined service messages (`respectContactSettings: false`)."
          }
        },
        "additionalProperties": false
      },
      "PublicSenderDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Use it in `channels` to send from exactly this sender, e.g. `email_42`.",
            "nullable": true
          },
          "channel": {
            "type": "string",
            "description": "`whatsapp`, `sms`, `rcs` or `email`.",
            "nullable": true
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "address": {
            "type": "string",
            "description": "What the customer sees as sender: the WhatsApp number, the mailbox address, the SMS sender name.",
            "nullable": true
          },
          "isDefault": {
            "type": "boolean",
            "description": "The sender meiti uses for this channel when none is named."
          }
        },
        "additionalProperties": false,
        "description": "One active sender of the key's inbox (GET v1/senders)."
      },
      "PublicUpdateAppointmentRequest": {
        "type": "object",
        "properties": {
          "start": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "durationMinutes": {
            "maximum": 1440,
            "minimum": 5,
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "title": {
            "maxLength": 120,
            "type": "string",
            "nullable": true
          },
          "confirmed": {
            "type": "boolean",
            "nullable": true
          },
          "externalId": {
            "maxLength": 100,
            "type": "string",
            "nullable": true
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "nullable": true
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Move or relabel. A new start or duration re-invites the customer unless `confirmed` says they already agreed."
      },
      "PublicUpsertContactRequest": {
        "required": [
          "phone"
        ],
        "type": "object",
        "properties": {
          "phone": {
            "maxLength": 40,
            "minLength": 1,
            "type": "string"
          },
          "email": {
            "maxLength": 320,
            "type": "string",
            "nullable": true
          },
          "salutation": {
            "maxLength": 100,
            "type": "string",
            "nullable": true
          },
          "firstName": {
            "maxLength": 100,
            "type": "string",
            "nullable": true
          },
          "lastName": {
            "maxLength": 100,
            "type": "string",
            "nullable": true
          },
          "company": {
            "maxLength": 200,
            "type": "string",
            "nullable": true
          },
          "serviceConsent": {
            "type": "boolean",
            "nullable": true
          },
          "marketingConsent": {
            "type": "boolean",
            "description": "Separate from `serviceConsent`: service never implies marketing.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Create or update a contact by address. The phone number is the identity: a known number updates that contact, an unknown one creates it. Name fields replace only when present; empty strings clear. A contact cannot be created from an email alone yet. `serviceConsent` / `marketingConsent` record what the customer told the caller's system about the phone number in this inbox: null leaves it untouched, true = granted, false = denied. Every change is kept as consent evidence (source: public API)."
      },
      "PublicWhatsAppTemplateDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true
          },
          "language": {
            "type": "string",
            "nullable": true
          },
          "category": {
            "type": "string",
            "description": "`MARKETING` or `UTILITY`, as WhatsApp classified it.",
            "nullable": true
          },
          "body": {
            "type": "string",
            "nullable": true
          },
          "variableCount": {
            "type": "integer",
            "description": "How many body variables (`{{1}}`…) `variables` must carry.",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "An approved WhatsApp template of the inbox's own number (GET v1/whatsapp/templates)."
      },
      "PublicWhatsAppTemplateRequest": {
        "required": [
          "template"
        ],
        "type": "object",
        "properties": {
          "template": {
            "maxLength": 512,
            "minLength": 1,
            "type": "string",
            "description": "Template name as approved on the inbox's WhatsApp Business number."
          },
          "language": {
            "maxLength": 15,
            "type": "string",
            "description": "Template language code as approved, e.g. `de` or `en_US`. Omitted = the template's only language.",
            "nullable": true
          },
          "variables": {
            "maxItems": 10,
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Values for the body variables `{{1}}`…`{{n}}`, in order. Must match the template's count.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A WhatsApp template for `whatsApp`."
      },
      "PublicWorkspaceInfo": {
        "type": "object",
        "properties": {
          "workspaceId": {
            "type": "integer",
            "format": "int32"
          },
          "workspaceName": {
            "type": "string",
            "nullable": true
          },
          "keyName": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Answer of GET v1/me — lets a caller verify a key and see which inbox it acts on."
      },
      "UpdateExternalAgentRequest": {
        "type": "object",
        "properties": {
          "chatUrl": {
            "maxLength": 2000,
            "type": "string",
            "nullable": true
          },
          "voiceUrl": {
            "maxLength": 2000,
            "type": "string",
            "nullable": true
          },
          "timeoutSeconds": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "voiceId": {
            "maxLength": 100,
            "type": "string",
            "description": "ElevenLabs voice id for phone calls; empty string = back to meiti's default voice.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Change an external agent's endpoints. Null leaves a field unchanged; an empty string disconnects it."
      },
      "WebhookSubscriptionDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int32"
          },
          "url": {
            "type": "string",
            "nullable": true
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "disabledAt": {
            "type": "string",
            "description": "Set when meiti stopped delivering after too many consecutive failures. Create a new subscription to resume.",
            "format": "date-time",
            "nullable": true
          },
          "consecutiveFailures": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      }
    },
    "securitySchemes": {
      "Bearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Json Web Token"
      },
      "ApiKeyHeader": {
        "type": "apiKey",
        "description": "Service key from the meiti app, e.g. meiti_sk_….",
        "name": "X-Api-Key",
        "in": "header"
      },
      "ApiKeyBearer": {
        "type": "http",
        "description": "The same service key as a Bearer token.",
        "scheme": "bearer"
      }
    }
  },
  "security": [
    {
      "ApiKeyHeader": [ ]
    },
    {
      "ApiKeyBearer": [ ]
    }
  ],
  "tags": [
    {
      "name": "Agents"
    },
    {
      "name": "Appointments"
    },
    {
      "name": "Contacts"
    },
    {
      "name": "Key"
    },
    {
      "name": "Log"
    },
    {
      "name": "Messages"
    },
    {
      "name": "Projects"
    },
    {
      "name": "Senders"
    },
    {
      "name": "Webhooks"
    }
  ]
}