{
  "openapi": "3.1.0",
  "info": {
    "title": "MessageHub API",
    "version": "v1",
    "description": "A public guide, no login needed, to connect your system to MessageHub: send WhatsApp, Messenger, Instagram, Telegram and TikTok messages, and receive customer replies with signed webhooks, with ready examples in cURL, Node.js, Python and PHP.\n\nDocumentation: https://derha.net/api-docs/en/. Full text for AI models: https://derha.net/llms-full.txt."
  },
  "servers": [
    {
      "url": "https://derha.net/api"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Messages"
    },
    {
      "name": "Account"
    },
    {
      "name": "Webhooks"
    }
  ],
  "paths": {
    "/v1/businesses": {
      "get": {
        "operationId": "businesses",
        "summary": "List businesses and channels",
        "description": "Returns every business in your workspace with its channels. Use each channel's number_id to choose the sending channel.",
        "tags": [
          "Account"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "4XX": {
            "description": "Client error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/businesses/{id}": {
      "get": {
        "operationId": "business",
        "summary": "Business details",
        "description": "Returns one business by id with its channels. A business outside your workspace answers 404 with the code business_not_found.",
        "tags": [
          "Account"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "4XX": {
            "description": "Client error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/numbers": {
      "get": {
        "operationId": "numbers",
        "summary": "Channels",
        "description": "The workspace's channels: id (the number_id), platform channel, public name handle where there is no phone, whether it may start a conversation can_initiate (any, known or none), and whether it is connected now. /v1/channels returns the same list under the key channels.",
        "tags": [
          "Account"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "4XX": {
            "description": "Client error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messages/send": {
      "post": {
        "operationId": "send",
        "summary": "Send a message",
        "description": "Queues a text message and returns 202 at once. Follow its status afterwards with the \"Message status\" endpoint or a webhook.",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "A unique value that prevents a duplicate send on retry.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "4XX": {
            "description": "Client error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "text"
                ],
                "properties": {
                  "number_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The sending channel id. Required with to or contact_id."
                  },
                  "channel_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Another name for number_id, for channels that are not phone numbers."
                  },
                  "conversation_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "An existing conversation on any platform. The message goes out on its channel, so number_id is not needed."
                  },
                  "contact_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "A contact with an identity on the chosen channel's platform. Their conversation is opened if needed."
                  },
                  "to": {
                    "type": "string",
                    "description": "Recipient number in international format WITH its country code, e.g. 201014642581. A leading + and spaces are accepted and stripped. A local number without a country code (01014642581) is rejected."
                  },
                  "text": {
                    "type": "string",
                    "description": "Message text, up to 4096 characters."
                  },
                  "type": {
                    "type": "string",
                    "description": "Message type. Currently only text, which is the default."
                  },
                  "transactional": {
                    "type": "boolean",
                    "description": "Set true when the message is about the customer's own order, booking or account (a receipt, a visit reminder, a code). Needed only for a customer who asked to stop messages. See \"Customers who asked to stop\"."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/custom-messages": {
      "get": {
        "operationId": "custom_messages",
        "summary": "Custom messages",
        "description": "The workspace's prepared messages and what each slot expects: whether it is filled from the customer's record or by you, and whether it is kept after sending. An id beginning with lib: is a platform starter message and is sent the same way. Messages are created in the dashboard only.",
        "tags": [
          "Account"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "4XX": {
            "description": "Client error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messages/send-custom": {
      "post": {
        "operationId": "send_custom",
        "summary": "Send a custom message",
        "description": "Sends a prepared message with its slots filled. Slots bound to the customer's record are resolved on the server and ignore anything you send for them. The recipient is named in the same three ways as the send endpoint.",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Always send it with verification codes: a retry with the same key does not send a second code.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "4XX": {
            "description": "Client error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "custom_message_id"
                ],
                "properties": {
                  "number_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The sending channel id. Required with to or contact_id."
                  },
                  "channel_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Another name for number_id, for channels that are not phone numbers."
                  },
                  "conversation_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "An existing conversation on any platform. The message goes out on its channel, so number_id is not needed."
                  },
                  "contact_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "A contact with an identity on the chosen channel's platform. Their conversation is opened if needed."
                  },
                  "to": {
                    "type": "string",
                    "description": "Recipient number in international format WITH its country code, e.g. 201014642581. A leading + and spaces are accepted and stripped. A local number without a country code (01014642581) is rejected."
                  },
                  "custom_message_id": {
                    "type": "string",
                    "description": "The custom message id, or lib:key for a starter message."
                  },
                  "values": {
                    "type": "object",
                    "description": "Slot values keyed by slot NUMBER (key \"2\", value \"482913\"). Not a list, because bound slots never appear here."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/messages/show/{id}": {
      "get": {
        "operationId": "status",
        "summary": "Message status",
        "description": "Full details of a message you sent through the API: status, every timestamp, failure information and the complete delivery history. Messages that did not originate from the API are not visible here (incoming messages have their own endpoint).",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "4XX": {
            "description": "Client error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messages/list": {
      "get": {
        "operationId": "messages",
        "summary": "List sent messages",
        "description": "A paginated summary of your API sends, newest first. For one message in full use /v1/messages/show/{id}.",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "name": "business_id",
            "in": "query",
            "required": false,
            "description": "Only messages of one business.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "number_id",
            "in": "query",
            "required": false,
            "description": "Only messages of one channel.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "queued, sent, delivered, read or failed.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number, starting at 1.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Items per page. Default 25, maximum 100.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "4XX": {
            "description": "Client error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messages/inbound": {
      "get": {
        "operationId": "inbound",
        "summary": "Incoming messages",
        "description": "Messages customers sent to your channels, newest first, in the same shape as message in a webhook. Use it to catch up on events you missed while your server was down, or if you prefer polling to webhooks.",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "name": "business_id",
            "in": "query",
            "required": false,
            "description": "One business.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "number_id",
            "in": "query",
            "required": false,
            "description": "One channel (or channel_id).",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "channel",
            "in": "query",
            "required": false,
            "description": "One platform: whatsapp, messenger, instagram, telegram or tiktok.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "description": "Only messages from this time on.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "until",
            "in": "query",
            "required": false,
            "description": "Only messages up to this time.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number, starting at 1.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Items per page. Default 25, maximum 100.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "4XX": {
            "description": "Client error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/media/{id}": {
      "get": {
        "operationId": "media",
        "summary": "Download a file",
        "description": "Returns the bytes of a file attached to a message (image, audio, video, document) at the url given in the attachment. It needs your API key like any other request.",
        "tags": [
          "Account"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "4XX": {
            "description": "Client error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/balance": {
      "get": {
        "operationId": "balance",
        "summary": "Balance",
        "description": "The message balance every send draws from, and the AI points. Check it before a large batch.",
        "tags": [
          "Account"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "4XX": {
            "description": "Client error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks/events": {
      "get": {
        "operationId": "wh_events",
        "summary": "Webhook events",
        "description": "The available events and what each one means.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "4XX": {
            "description": "Client error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks": {
      "get": {
        "operationId": "wh_list",
        "summary": "List webhooks",
        "description": "Every webhook in the workspace: URL, events, channels and whether it is suspended. Secrets are never shown. Webhooks are created and edited in the dashboard only.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "4XX": {
            "description": "Client error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks/{id}/test": {
      "post": {
        "operationId": "wh_test",
        "summary": "Send a test event",
        "description": "Sends a webhook.test event to the given webhook URL. It is sent even while the webhook is suspended, and success resumes it. An id outside your workspace answers webhook_not_found.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "4XX": {
            "description": "Client error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks/deliveries": {
      "get": {
        "operationId": "wh_deliveries",
        "summary": "Delivery log",
        "description": "Events sent by the workspace's webhooks, newest first: status, your server's response, attempts and the next retry.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "webhook_id",
            "in": "query",
            "required": false,
            "description": "One webhook only.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "success, failed, pending or queued.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "event",
            "in": "query",
            "required": false,
            "description": "An event type, such as message.received.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "4XX": {
            "description": "Client error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks/deliveries/{id}/redeliver": {
      "post": {
        "operationId": "wh_redeliver",
        "summary": "Resend an event",
        "description": "Sends the event again with the same body and the same event id, so your server can tell it already handled it. The event carries the body built when it happened, never a newer version. Limit: 10 resends a minute per webhook.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "4XX": {
            "description": "Client error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key token (starts with mh_live), created in the dashboard under Developer API, Keys."
      }
    },
    "schemas": {
      "Envelope": {
        "type": "object",
        "required": [
          "success"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "message": {
            "type": "string"
          },
          "data": {
            "type": "object"
          },
          "meta": {
            "type": "object",
            "description": "pagination: total, per_page, current_page, last_page"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "success",
          "code"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "code": {
            "type": "string",
            "enum": [
              "unauthenticated",
              "origin_not_allowed",
              "insecure_transport",
              "ip_not_allowed",
              "business_not_found",
              "number_not_found",
              "conversation_not_found",
              "contact_not_found",
              "media_not_found",
              "webhook_not_found",
              "message_not_found",
              "custom_message_not_found",
              "number_not_connected",
              "media_not_ready",
              "request_in_progress",
              "contact_opted_out",
              "validation_failed",
              "slot_value_missing",
              "contact_not_on_channel",
              "cannot_initiate_on_channel",
              "reply_window_closed",
              "human_only_window",
              "message_limit_reached",
              "outside_service_window",
              "text_too_long_for_channel",
              "media_not_supported_on_channel",
              "media_too_large_for_channel",
              "text_with_media_not_supported",
              "idempotency_key_reused",
              "quota_exhausted",
              "api_not_enabled",
              "rate_limited",
              "server_error"
            ],
            "description": "Stable code. Branch on this."
          },
          "message": {
            "type": "string"
          },
          "errors": {
            "type": "object",
            "description": "Per-field details on validation_failed."
          }
        }
      }
    }
  }
}
