diff --git a/connections/connection-form.mdx b/connections/connection-form.mdx
index 56ced1b..7d46986 100644
--- a/connections/connection-form.mdx
+++ b/connections/connection-form.mdx
@@ -199,6 +199,7 @@ Metadata connections are the extra ones opened to read a database's object list
| [Elasticsearch](/databases/elasticsearch) | 9200 | Yes | Yes | Yes | No | Yes | Yes |
| [Typesense](/databases/typesense) | 8108 | Yes | Yes | Yes | No | Yes | Yes |
| [Weaviate](/databases/weaviate) | 8080 | Yes | Yes | Yes | No | Yes | Yes |
+| [Qdrant](/databases/qdrant) | 6333 | Yes | Yes | Yes | No | Yes | Yes |
| [SAP HANA](/databases/sap-hana) | 443 | No | Yes | No | No | No | No |
| [Snowflake](/databases/snowflake) | 443 | No | No | No | No | No | No |
| [SQLite](/databases/sqlite) | File | No | No | No | No | No | No |
diff --git a/connections/ssl.mdx b/connections/ssl.mdx
index 1eb790a..a73811d 100644
--- a/connections/ssl.mdx
+++ b/connections/ssl.mdx
@@ -26,7 +26,7 @@ The certificate rows appear only once **SSL Mode** is anything but Disabled. Eac
| Field | When it appears | What it takes |
|---|---|---|
-| **Certificate**, under **CA Certificate** | Verify CA and Verify Identity only | The PEM or DER certificate the server's certificate chains to, for example `/path/to/ca-cert.pem`. Saving without it is blocked, except under Verify Identity on ClickHouse, Kafka, Presto, SAP HANA and Trino, which check the system trust store when it is empty |
+| **Certificate**, under **CA Certificate** | Verify CA and Verify Identity only | The PEM or DER certificate the server's certificate chains to, for example `/path/to/ca-cert.pem`. Saving without it is blocked, except under Verify Identity on ClickHouse, Kafka, Presto, Qdrant, SAP HANA and Trino, which check the system trust store when it is empty |
| **Client Certificate** | Any mode but Disabled | Your certificate, for a server that enforces mutual TLS. Optional otherwise |
| **Client Key** | Any mode but Disabled | The matching private key. Required once a client certificate is set |
| **Key Passphrase** | Once **Client Key** has a path, on drivers that support it (Cassandra, ScyllaDB and MongoDB) | The passphrase of an encrypted key. Stored in the Keychain |
@@ -43,7 +43,7 @@ A new connection starts on the mode that matches the driver's own default, and s
| MySQL, MariaDB | Preferred | TLS when the server offers it, plain when it does not, and a plain retry after an SSL handshake error. Auth and network errors are not retried |
| SQL Server | Preferred | FreeTDS `encryption=request`: the login over TLS, the rest in plain TCP unless the server forces encryption |
| Teradata | Disabled | Opens a TLS transport, retries on a plain socket if it fails to come up |
-| MongoDB, Redis, Cassandra, ClickHouse, Elasticsearch, Typesense, SurrealDB, Weaviate | Disabled | Nothing. No fallback exists, so Preferred forces TLS exactly like Required |
+| MongoDB, Redis, Cassandra, ClickHouse, Elasticsearch, Typesense, SurrealDB, Weaviate, Qdrant | Disabled | Nothing. No fallback exists, so Preferred forces TLS exactly like Required |
| etcd | Disabled | Nothing. The driver never reads these fields. Set **TLS Mode** on the Options section instead, and see [etcd](/databases/etcd) |
| Trino, Presto | Disabled | Sends every request over HTTPS with no fallback, again like Required |
| SAP HANA | Verify Identity | Nothing. No fallback exists, so Preferred forces TLS exactly like Required |
diff --git a/databases/index.mdx b/databases/index.mdx
index ba17ca4..85d42a6 100644
--- a/databases/index.mdx
+++ b/databases/index.mdx
@@ -1,11 +1,11 @@
---
title: Supported Databases
-description: All 44 engines TablePro connects to, their default ports, and which ones need a plugin
+description: All 47 engines TablePro connects to, their default ports, and which ones need a plugin
---
import DriverCounts from "/snippets/driver-counts.mdx";
-Forty-four engines, and every one of them is free to use. What differs between them is where the
+Forty-seven engines, and every one of them is free to use. What differs between them is where the
driver comes from, not what the license covers.
@@ -46,6 +46,7 @@ driver comes from, not what the license covers.
| [PGlite](/databases/pglite) | 5432 | Built-in |
| [PostgreSQL](/databases/postgresql) | 5432 | Built-in |
| [Presto](/databases/trino) | 8080 | Plugin |
+| [Qdrant](/databases/qdrant) | 6333 | Plugin |
| [Redis](/databases/redis) | 6379 | Built-in |
| [SAP HANA](/databases/sap-hana) | 443 | Plugin |
| [ScyllaDB](/databases/cassandra) | 9042 | Plugin |
diff --git a/databases/qdrant.mdx b/databases/qdrant.mdx
new file mode 100644
index 0000000..a13bc8d
--- /dev/null
+++ b/databases/qdrant.mdx
@@ -0,0 +1,134 @@
+---
+title: Qdrant
+description: Connect to Qdrant, browse collections as points, edit payloads and vectors by id, and run REST requests
+---
+
+import RegistryPlugin from "/snippets/registry-plugin.mdx";
+
+Collections are tables, points are rows, and the point `id` is the primary key. The editor takes Qdrant REST requests, not SQL, so a vector search such as `POST /collections/docs/points/query` runs from the editor and its points and scores land in the grid.
+
+
+
+## Quick setup
+
+Click **New Connection…**, select **Qdrant**, enter host and port, paste an **API Key** if the node needs one, then click **Save & Connect**.
+
+There is no Database field. One connection reaches one Qdrant instance, and its collections are the objects.
+
+For a node on a private server, open the **Network** tab and set **Connect via** to **SSH Tunnel**, then set **Host** to the address Qdrant listens on as the SSH server sees it, usually `localhost`. See [SSH Tunneling](/connections/ssh-tunneling).
+
+## Connection settings
+
+| Field | Description |
+|-------|-------------|
+| **Host** | Node hostname. `localhost` for Docker, the cluster URL's host for Qdrant Cloud |
+| **Port** | `6333` by default, the REST port. Qdrant Cloud also answers on `6333` |
+| **API Key** | Sent as the `api-key` header. Leave it empty for a node started without `QDRANT__SERVICE__API_KEY` |
+
+Qdrant Cloud requires the API key and HTTPS. Set **SSL Mode** to **Verify Identity** with no CA certificate: the certificate is checked against the system trust store.
+
+## Browsing collections
+
+The sidebar lists collections from `GET /collections`.
+
+Every grid has `id` first and the vectors last. `id` is the primary key and read-only. A collection with one unnamed vector shows a `vector` column. A collection with named or sparse vectors shows one column per name, such as `vector.image`. A dense vector is a JSON number array, a multivector an array of them, and a sparse vector its `indices` and `values`.
+
+Payload columns are the top-level indexed fields, then the fields found in the first 100 points. Arrays and objects render as JSON in the cell. An index on a nested path such as `metadata.source` or `tags[]` is listed in the **Indexes** section of the **Structure** tab, and its value shows inside the `metadata` or `tags` column. A field that appears only in later points shows up in a query result but not as a grid column.
+
+Pages load through `POST /points/query` in id order, with the offset applied by Qdrant, so a page always shows the rows at that position now. A node older than 1.10 has no Query API, so its pages walk the point ids from the start.
+
+## Sorting
+
+Sort by one payload field that has an `integer`, `float` or `datetime` payload index. Points with a value in that field come first, in order. Points without one follow, by id. Every point stays reachable, and the row count still matches the collection.
+
+Sorting by `id` ascending is the default order. These sorts are refused with a message:
+
+- `id` descending, a vector, or more than one column.
+- A field that holds a list in any point, since Qdrant would list that point once per value.
+
+## Filtering
+
+A payload index does not limit what a field holds, so a value matches every type it can be: `42` finds the text `"42"`, the number 42, and a list holding either. The index still speeds up the part of the filter of its own type.
+
+| Operators | What Qdrant runs |
+|----------|------------------|
+| equals, not equals | `match.value` for the text, plus a closed `range` when it is a number and a boolean match for `true`, `false`, `1` or `0` |
+| greater than, greater or equal, less than, less or equal, between | `range`, on numbers, or on dates for any other text |
+| in list, not in list | Equals for each value, collapsed to `match.any` when every value is plain text |
+| contains, not contains | `match.text`, with or without a full-text index. It matches whole words and ignores case, so `world` finds `Hello World` but `lo Wo` finds nothing. A number, a boolean or a vector matches nothing |
+| is NULL, is not NULL | `is_empty`: the key is missing, `null`, or an empty list |
+| is empty, is not empty | `is_empty`, or the empty string on a text field |
+
+Not equals, not in list and not contains also match points the key is missing from. On `id`, equals and in list become `has_id`, and contains finds a whole id only. **Starts with**, **ends with** and **REGEX** have no Qdrant equivalent, and a filter Qdrant cannot run reports why instead of returning the whole collection. The raw filter row takes one Qdrant condition as JSON, such as `{"key": "city", "match": {"value": "Berlin"}}`.
+
+Filtered row counts come from `POST /points/count` with `exact` set.
+
+## Editing points
+
+Grid edits are REST requests keyed by `id`, shown in the review sheet before they run.
+
+| Change | Request |
+|--------|---------|
+| New row | `POST /collections/{name}/points` first, to check that no point has the id, then `PUT /collections/{name}/points` with an `update_filter` that matches no point, so an id taken in between is left alone. An empty `id` gets a new UUID. Vector cells you fill are stored with the point. Needs Qdrant 1.15 or later, since older nodes ignore the filter and overwrite the point |
+| Edited payload cells | `POST /collections/{name}/points/payload`, which sets only the edited keys |
+| Edited vector cells | `PUT /collections/{name}/points/vectors`. Setting a vector to NULL sends `POST /collections/{name}/points/vectors/delete` |
+| Deleted row | `POST /collections/{name}/points/delete` |
+
+A cell is written back as the type its column holds: a `keyword` field stays a string even when it reads `42`, and a `bool` field takes `true`, `false`, `1` or `0`. A column whose points hold different types, such as text in one and a number in another, has the type `json`: its text shows in quotes, and a cell saves as the JSON it spells, so `"42"` stays text and `42` a number. A value that does not fit, such as `4.5` in an `integer` column or a vector that is not a number array, stops the save and names the column. **Truncate** deletes every point with an empty filter and keeps the collection. **Drop** removes the collection.
+
+## REST editor
+
+Type a request line, then a JSON body on the same line, on the lines under it, or starting on the line and going on under it:
+
+```http
+POST /collections/docs/points/query
+{"query": [0.1, 0.2, 0.3, 0.4], "limit": 10, "with_payload": true}
+```
+
+A response that lists points, from `query`, `scroll`, `search` or a retrieve by id, renders as a grid with `id`, then `score` when there is one, the payload fields, and any vectors returned. Anything else is shown as formatted JSON.
+
+Read-only mode allows `GET`, `POST /collections/{name}/facet`, and the read endpoints under `/points`: `query`, `scroll`, `search`, `count`, `recommend`, `discover`, their batch and group forms, and a retrieve by id. A `DELETE`, a path ending in `delete` or `clear`, and `points/batch` count as destructive. A snapshot request is flagged as reaching the server's disk.
+
+## SSL/TLS
+
+New connections start on **Disabled**, plain HTTP. Every other mode goes over HTTPS.
+
+| SSL Mode | What is checked |
+|----------|-----------------|
+| Preferred, Required (skip verify) | Nothing. A self-signed certificate is accepted |
+| Verify CA | The certificate chains to the CA certificate you choose and is issued for a server. The hostname is not checked |
+| Verify Identity | The certificate chains to the CA certificate, or to the system trust store when none is chosen, and names the host |
+
+For a node that enforces mutual TLS, choose a **Client Certificate** and its **Client Key**. See [SSL/TLS](/connections/ssl).
+
+## Limitations
+
+- SQL is not available. Use the REST editor.
+- gRPC is not used.
+- Payload indexes and collection settings are not edited from Structure. Send the REST request, such as `PUT /collections/{name}/index`.
+- A field that holds a list in any point cannot be sorted.
+- Adding rows from the grid needs Qdrant 1.15 or later. On an older node, upsert from the REST editor.
+- The plugin is registry-only. It is not on iPhone or iPad.
+
+## Troubleshooting
+
+### Must provide an API key
+
+The node was started with an API key and **API Key** is empty. Paste the key and connect again.
+
+### Invalid API key or JWT
+
+The key was rejected. The health check reads `/collections`, which needs the key, so a revoked key also drops a live session.
+
+### No range index for `order_by` key
+
+The sorted column has no `integer`, `float` or `datetime` payload index. Create one with `PUT /collections/{name}/index`, or clear the sort.
+
+### A row with this key already exists
+
+A new row's `id` is already taken. Clear the `id` cell to get a new UUID, or edit the existing point instead.
+
+## Related
+
+- [Import & Export](/features/import-export)
+- [Filtering](/features/filtering)
diff --git a/docs.json b/docs.json
index 8cea481..cbb9098 100644
--- a/docs.json
+++ b/docs.json
@@ -268,6 +268,7 @@
"databases/oracle",
"databases/pglite",
"databases/postgresql",
+ "databases/qdrant",
"databases/redis",
"databases/sap-hana",
"databases/snowflake",
diff --git a/index.mdx b/index.mdx
index edeb2d1..be3e258 100644
--- a/index.mdx
+++ b/index.mdx
@@ -1,6 +1,6 @@
---
title: Introduction
-description: Native macOS database client for MySQL, PostgreSQL, SQLite, MongoDB, Redis, and 39 more
+description: Native macOS database client for MySQL, PostgreSQL, SQLite, MongoDB, Redis, and 42 more
---
import DriverCounts from "/snippets/driver-counts.mdx";
diff --git a/snippets/driver-counts.mdx b/snippets/driver-counts.mdx
index c8f8155..7bc9ec3 100644
--- a/snippets/driver-counts.mdx
+++ b/snippets/driver-counts.mdx
@@ -1,3 +1,3 @@
-Five drivers ship inside the app and cover twelve databases. Twenty-eight registry plugins cover the
-other thirty-two and install on the first connection that needs one. See
+Five drivers ship inside the app and cover twelve databases. Thirty-one registry plugins cover the
+other thirty-five and install on the first connection that needs one. See
[Plugins](/features/plugins).