{
  "openapi": "3.0.0",
  "info": {
    "title": "Wapiworld API",
    "version": "1.0.0",
    "description": "Public REST API for Wapiworld — WhatsApp API as a service. Authenticate with an API key created in the Wapiworld dashboard: send the base64-encoded key secret with HTTP Basic auth. Each key is scoped to a single project and carries per-resource scopes like `instances:read` or `instances:write`."
  },
  "servers": [
    {
      "url": "https://api.wapiworld.com"
    }
  ],
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "basic",
        "description": "HTTP Basic auth carrying only the API key secret: `Authorization: Basic base64(<key secret>)`."
      },
      "accessToken": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "Operator session token issued by the Wapiworld dashboard: `Authorization: Token <access token>`."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "message",
          "status"
        ],
        "properties": {
          "details": {
            "type": "object",
            "additionalProperties": true,
            "description": "Optional structured values needed to render the error."
          },
          "message": {
            "type": "string"
          },
          "method": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "Instance": {
        "type": "object",
        "required": [
          "_id",
          "projectId"
        ],
        "properties": {
          "_id": {
            "type": "string"
          },
          "projectId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "Current connection status reported by the instance."
          },
          "creationTime": {
            "type": "string",
            "format": "date-time"
          },
          "lastEditTime": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "InstanceActionResult": {
        "nullable": true,
        "description": "Action-specific JSON returned by the connected instance, or null when the action has no response body.",
        "oneOf": [
          {
            "type": "object",
            "additionalProperties": true
          },
          {
            "type": "array",
            "items": {}
          },
          {
            "type": "string"
          },
          {
            "type": "boolean"
          },
          {
            "type": "number"
          }
        ]
      },
      "IdempotentSendResult": {
        "type": "object",
        "required": [
          "key",
          "messageTimestamp"
        ],
        "properties": {
          "key": {
            "type": "object",
            "required": [
              "id",
              "remoteJid",
              "fromMe"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "remoteJid": {
                "type": "string"
              },
              "fromMe": {
                "type": "boolean"
              }
            },
            "additionalProperties": false
          },
          "messageTimestamp": {
            "type": "integer",
            "format": "int64"
          }
        },
        "additionalProperties": false
      },
      "RecordedMessage": {
        "type": "object",
        "required": [
          "_id",
          "instanceId",
          "creationTime"
        ],
        "properties": {
          "_id": {
            "type": "string"
          },
          "instanceId": {
            "type": "string"
          },
          "chatId": {
            "type": "string",
            "description": "Normalized counterparty key."
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound"
            ]
          },
          "creationTime": {
            "type": "string",
            "format": "date-time"
          },
          "lastEditTime": {
            "type": "string",
            "format": "date-time"
          },
          "expiryTime": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "Project": {
        "type": "object",
        "required": [
          "_id"
        ],
        "properties": {
          "_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "creationTime": {
            "type": "string",
            "format": "date-time"
          },
          "lastEditTime": {
            "type": "string",
            "format": "date-time"
          },
          "messageRetentionDays": {
            "type": "integer",
            "minimum": 1
          }
        },
        "additionalProperties": true
      },
      "WebhookSubscription": {
        "type": "object",
        "required": [
          "_id",
          "projectId",
          "url",
          "active"
        ],
        "properties": {
          "_id": {
            "type": "string"
          },
          "projectId": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "instance.created",
                "instance.connected",
                "instance.disconnected",
                "instance.deleted"
              ]
            }
          },
          "active": {
            "type": "boolean"
          },
          "consecutiveFailures": {
            "type": "integer",
            "minimum": 0
          },
          "creationTime": {
            "type": "string",
            "format": "date-time"
          },
          "lastEditTime": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "WebhookSubscriptionWithSecret": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookSubscription"
          },
          {
            "type": "object",
            "required": [
              "secret"
            ],
            "properties": {
              "secret": {
                "type": "string",
                "readOnly": true,
                "description": "Signing secret returned exactly once in the create response."
              }
            }
          }
        ]
      },
      "DeletedResource": {
        "type": "object",
        "required": [
          "_id"
        ],
        "properties": {
          "_id": {
            "type": "string"
          }
        },
        "additionalProperties": false
      }
    }
  },
  "paths": {
    "/api/instances": {
      "get": {
        "tags": [
          "Instances"
        ],
        "summary": "List WhatsApp instances",
        "description": "Lists the WhatsApp instances of your organization. An API key is scoped to a single project and only sees that project's instances. Requires the `instances:read` scope.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "projectId",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "fields",
            "description": "Comma-separated projection of fields to return",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "description": "Optional page size, capped at 500",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500
            }
          },
          {
            "in": "query",
            "name": "skip",
            "description": "Optional zero-based page offset",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Array of instances",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Instance"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `instances:read` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getApiInstances"
      },
      "post": {
        "tags": [
          "Instances"
        ],
        "summary": "Create a WhatsApp instance",
        "description": "Creates a new WhatsApp instance in your project. A caller-supplied `_id`, whether in the body or `instanceId` query, requires a stable Idempotency-Key; retrying the same create (including the same write-only webhook signing secret) returns the same resource, while reusing either identity for a different intent returns 409. The first accepted command pins its runtime backend, so an exact retry resumes the same command even if a deployment gate changed meanwhile. Keyed creates reserve the resource in stopped state and must be started with the reconnect action after the caller durably stores the returned id. Unkeyed legacy creates retain their historical auto-start behavior. Requires the `instances:write` scope and emits `instance.created`.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "description": "Stable 16-128 character key. Required when the request body supplies `_id` or the query supplies `instanceId`; recommended for every automated create.\n",
            "schema": {
              "type": "string",
              "minLength": 16,
              "maxLength": 128
            }
          },
          {
            "in": "header",
            "name": "Wapiworld-Required-Create-Backend-Protocol",
            "required": false,
            "description": "Fail before reservation unless the selected create backend exactly matches this protocol",
            "schema": {
              "type": "string",
              "enum": [
                "durable-generation-v1",
                "legacy-direct-v1"
              ]
            }
          },
          {
            "in": "query",
            "name": "instanceId",
            "required": false,
            "description": "Optional caller-supplied 24-character lowercase hex resource id",
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{24}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The created or exactly replayed instance",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Instance"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `instances:write` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The identity/key conflicts, a legacy effect lease is retryably busy, or runtime capacity is full",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The explicitly required create backend is not currently selected",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "postApiInstances"
      }
    },
    "/api/instances/{instanceId}": {
      "get": {
        "tags": [
          "Instances"
        ],
        "summary": "Get a WhatsApp instance",
        "description": "Returns a single instance by id. Requires the `instances:read` scope. Answers 404 when the instance belongs to another organization or project.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "instanceId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The instance",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Instance"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `instances:read` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Instance not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getApiInstancesInstanceId"
      },
      "put": {
        "tags": [
          "Instances"
        ],
        "summary": "Update a WhatsApp instance",
        "description": "Updates an existing instance. A body containing `webhookSigningSecret` is always an atomic durable configuration command and requires a stable Idempotency-Key. Its exact public patch and write-only secret may be replayed until the returned configurationOperation reaches `completed`; changing that intent for the same key returns 409. Requires the `instances:write` scope. May emit `instance.connected` / `instance.disconnected` on a status transition.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "instanceId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "description": "Required for a body containing webhookSigningSecret; stable 16-128 character operation key",
            "schema": {
              "type": "string",
              "minLength": 16,
              "maxLength": 128
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The updated instance and completed configuration operation, or an ordinary synchronous update",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Instance"
                }
              }
            }
          },
          "202": {
            "description": "The atomic configuration command is accepted and awaiting exact runtime acknowledgement"
          },
          "403": {
            "description": "API key is missing the `instances:write` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The idempotency key conflicts or the operation was superseded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "putApiInstancesInstanceId"
      },
      "delete": {
        "tags": [
          "Instances"
        ],
        "summary": "Delete a WhatsApp instance",
        "description": "Durably requests logout, provider-state purge, and deletion. Repeat the same DELETE to poll the intrinsic `delete:{instanceId}` operation; 202 is pending and 200 includes the immutable completion event. Requires the `instances:write` scope and emits `instance.deleted` exactly once. Answers 404 for an instance in another organization or project.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "instanceId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deletion completed; response includes the stable operation and event",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Instance"
                }
              }
            }
          },
          "202": {
            "description": "Deletion was durably accepted and is still pending"
          },
          "403": {
            "description": "API key is missing the `instances:write` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Instance not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A transitional create external effect is still in flight; retry without changing intent",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteApiInstancesInstanceId"
      }
    },
    "/api/instances/{instanceId}/action/send-message-idempotent": {
      "post": {
        "tags": [
          "Instances"
        ],
        "summary": "Send a WhatsApp message with a durable idempotency key",
        "description": "Requires one stable Idempotency-Key and the `instances:write` scope. Reuse the key only for the exact same instance, normalized chat, and content. An exact retry reuses one deterministic WhatsApp message id; changed intent returns 409. The unversioned `send-message` action is legacy and rejects every Idempotency-Key.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "instanceId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "description": "Required exactly once; retries of one command must reuse the exact value",
            "schema": {
              "type": "string",
              "minLength": 16,
              "maxLength": 128
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The action result from the instance",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IdempotentSendResult"
                }
              }
            }
          },
          "400": {
            "description": "The Idempotency-Key is present but invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `instances:write` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Instance not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The key is already bound to another send intent",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "A keyed send is unavailable or provider outcome is uncertain; retry the exact command and key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "The send timed out with an ambiguous outcome; retry the exact command and key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "postApiInstancesInstanceIdActionSendMessageIdempotent"
      }
    },
    "/api/instances/{instanceId}/action/{action}": {
      "post": {
        "tags": [
          "Instances"
        ],
        "summary": "Perform a legacy or non-send action on a WhatsApp instance",
        "description": "Drives actions such as `logout`, `reconnect`, and the legacy unkeyed `send-message`. Every Idempotency-Key is rejected on this route. Use the exact `send-message-idempotent` path for durable keyed sends.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "instanceId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "action",
            "required": true,
            "schema": {
              "type": "string",
              "example": "send-message"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The action result from the instance",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InstanceActionResult"
                }
              }
            }
          },
          "202": {
            "description": "A reconnect was durably accepted for background reconciliation"
          },
          "400": {
            "description": "An Idempotency-Key was supplied to a legacy or non-send action",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `instances:write` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Instance not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Reconnect capacity is unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The instance action could not be completed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "postApiInstancesInstanceIdActionAction"
      }
    },
    "/api/messages": {
      "get": {
        "tags": [
          "Messages"
        ],
        "summary": "List recorded WhatsApp messages",
        "description": "Reads the recorded messages of one instance, optionally filtered by chat. Message recording is opt-in per instance and TTL-expired. Requires the `messages:read` scope.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "instanceId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "chatId",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "startTime",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "endTime",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "default": 100
            }
          },
          {
            "in": "query",
            "name": "skip",
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Array of recorded messages",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/RecordedMessage"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `messages:read` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getApiMessages"
      }
    },
    "/api/projects": {
      "get": {
        "tags": [
          "Projects"
        ],
        "summary": "List projects",
        "description": "Lists your organization's projects. An API key is scoped to a single project and returns only that one. Requires the `projects:read` scope.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "fields",
            "description": "Optional comma-separated top-level fields to return",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Array of projects",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Project"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `projects:read` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getApiProjects"
      }
    },
    "/api/projects/{projectId}": {
      "get": {
        "tags": [
          "Projects"
        ],
        "summary": "Get a project",
        "description": "Returns a single project by id. Requires the `projects:read` scope. Answers 404 for a project in another organization, or for a project-scoped key asking for a different project.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The project",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `projects:read` scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Project not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getApiProjectsProjectId"
      }
    },
    "/api/webhooksubscriptions": {
      "get": {
        "tags": [
          "Webhook subscriptions"
        ],
        "summary": "List webhook subscriptions",
        "description": "Webhook subscriptions deliver `instance.created`, `instance.connected`, `instance.disconnected` and `instance.deleted` events to your server as signed POST requests (`X-Wapiworld-Signature: t=<timestamp>,v1=<hex HMAC-SHA256 of \"timestamp.body\">`). An endpoint failing 20 times in a row is disabled automatically. Subscriptions are managed with an operator access token. Responses contain only the reviewed public subscription fields; the `secret` is returned once, on create.\n",
        "security": [
          {
            "accessToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Array of webhook subscriptions (without secrets)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WebhookSubscription"
                  }
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getApiWebhooksubscriptions"
      },
      "post": {
        "tags": [
          "Webhook subscriptions"
        ],
        "summary": "Create a webhook subscription",
        "description": "The response includes the signing `secret` exactly once — store it; it cannot be retrieved again.\n",
        "security": [
          {
            "accessToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "projectId",
                  "url"
                ],
                "properties": {
                  "projectId": {
                    "type": "string"
                  },
                  "url": {
                    "type": "string",
                    "example": "https://example.com/wapiworld-webhook"
                  },
                  "events": {
                    "type": "array",
                    "description": "Empty array subscribes to all events",
                    "items": {
                      "type": "string",
                      "enum": [
                        "instance.created",
                        "instance.connected",
                        "instance.disconnected",
                        "instance.deleted",
                        "instance.qrcode.available",
                        "instance.qrcode.expired",
                        "instance.updated",
                        "message.received"
                      ]
                    }
                  },
                  "deliveryVersion": {
                    "type": "integer",
                    "enum": [
                      1,
                      2
                    ],
                    "description": "Version 1 is the legacy envelope; version 2 is the canonical CloudEvent"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The created subscription, including its secret",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSubscriptionWithSecret"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "postApiWebhooksubscriptions"
      }
    },
    "/api/webhooksubscriptions/{webhookSubscriptionId}": {
      "put": {
        "tags": [
          "Webhook subscriptions"
        ],
        "summary": "Update a webhook subscription",
        "description": "`url`, `events`, `deliveryVersion` and `active` are editable; the secret and project are immutable. Re-enabling an auto-disabled endpoint is done by setting `active` back to true.\n",
        "security": [
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "webhookSubscriptionId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated subscription (without secret)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSubscription"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "putApiWebhooksubscriptionsWebhookSubscriptionId"
      },
      "delete": {
        "tags": [
          "Webhook subscriptions"
        ],
        "summary": "Delete a webhook subscription",
        "security": [
          {
            "accessToken": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "webhookSubscriptionId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletedResource"
                }
              }
            }
          },
          "default": {
            "description": "Request failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteApiWebhooksubscriptionsWebhookSubscriptionId",
        "description": "Delete a webhook subscription"
      }
    }
  },
  "tags": []
}
