> ## Documentation Index
> Fetch the complete documentation index at: https://docs.noetive.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Register a persistent subscription and open an SSE match stream

> Compiles the SemQL query, registers a persistent subscription, and
returns a Server-Sent Events stream of match notifications.

The stream begins with a `subscribed` event whose `data` is a
`SubscribedEvent` JSON object. Subsequent `match` events carry a
`MatchEvent` JSON object. The subscription is removed automatically
when the client disconnects.

Example SSE frames:

```
event: subscribed
data: {"subscription_id":"sub_8f2c..."}

event: match
data: {"message_id":"msg_7a21de40f1b2c493","score":0.82}
```

Requires `Content-Type: application/json`.




## OpenAPI

````yaml /api/public-api.yaml post /v1/subscribe
openapi: 3.0.3
info:
  title: Noetive Semantik API
  version: 0.4.0
  description: >
    Noetive Semantik is a managed semantic search and subscription service.

    Publish messages tagged with embedding vectors, query them with the

    SemQL query language, and subscribe to live match streams over

    Server-Sent Events.


    All request and response bodies are JSON. The `/v1/subscribe` endpoint

    accepts a JSON POST request and upgrades the response to an SSE stream

    of match events.


    ## Request decoding


    Every JSON endpoint decodes strictly. A request is rejected with

    `400 invalid_request` when the body is empty, is not valid JSON,

    **carries a field this specification does not define**, or carries

    trailing content after the first JSON value. Unknown fields are an

    error rather than being ignored, so a misspelled field name fails

    loudly instead of silently taking a default.


    ## SemQL query limits


    These bound the `query` field on `/v1/search`, `/v1/subscribe`, and

    `/v1/lint`. Search and subscribe reject an over-limit query with

    `400 invalid_request` while parsing it — before any anchor is embedded,

    so a rejected query is not billed for embedding. `/v1/lint` reports the

    same problems as diagnostics with `valid: false` and a `200`, which

    makes it the cheap way to check a generated query before sending it.


    | limit | value |

    | --- | --- |

    | anchors per query, across all clauses | 32 |

    | anchors in one list (`DIRECTION` toward, `ATTRACT`, `REPEL`) | 8 |

    | namespace names in `NAMESPACE` | 8 |

    | namespace names in the `NOT` exclusion list | 8 |

    | floats in one vector anchor | 4096 |

    | floats across all vector anchors in one query | 8192 |

    | anchor text plus namespace names, combined | 4 KB |

    | nodes per query (clauses plus boolean operators) | 64 |

    | operands per `AND` or `OR` | 16 |

    | parenthesis nesting depth | 128 |


    Value ranges: `WITHIN` in `[0, 1]`, `CONE` in `[0, π]` radians, `LIMIT`

    and `WINDOW` strictly positive. `WITHIN` and `TOP` are mutually

    exclusive on one clause. `LIMIT` is the exception to the reject rule —

    see `SearchRequest.limit`.


    ## Request correlation


    Every response — success or error — carries an `X-Request-Id` header

    with a server-assigned correlation token. Error response bodies repeat

    the same value in the `request_id` field so it survives client-side

    logging that strips headers. Quote `request_id` when contacting

    support; it pivots directly to the corresponding server log line.


    Inbound `X-Request-Id` headers from clients are ignored — the server

    assigns the value authoritatively to prevent log injection. When the

    request carries an `X-Amzn-Trace-Id` header, its `Root=` segment is

    used as the request id so upstream trace correlation works without

    translation. Otherwise a fresh server-side identifier is minted.


    ## Error codes


    Error responses carry a stable machine-readable `error` code, an

    optional human-readable `message`, the server-assigned `request_id`,

    and (for retryable failures) a `retry_after_ms` hint. Known codes:


    | code | meaning |

    | --- | --- |

    | `invalid_request` | request body failed validation |

    | `unauthorized` | missing or invalid bearer token |

    | `not_found` | no endpoint at the request path — check the base URL; do not
    retry |

    | `idempotency_key_conflict` | the `idempotency_key` is live from an earlier
    publish carrying different content; nothing was stored |

    | `unsupported_media_type` | `Content-Type` is not `application/json` |

    | `request_too_large` | request body exceeds the endpoint size limit |

    | `rate_limited` | per-client burst or rate limit exhausted |

    | `too_many_requests` | concurrency limit exhausted, or the namespace is at
    its concurrent-subscription cap |

    | `unavailable` | service temporarily unavailable; retry after
    `retry_after_ms` |

    | `namespace_unavailable` | namespace lookup is transiently unavailable;
    retry after `retry_after_ms` |

    | `namespace_disabled` | the namespace alias exists but has been
    administratively disabled |

    | `model_not_provisioned` | the namespace alias exists but has no entry for
    the requested (model, dimensions) combination |

    | `not_billable` | no active subscription on the account; resolve billing
    before retrying |

    | `metering_unavailable` | metered billing is transiently unavailable; retry
    with backoff |

    | `internal_error` | unexpected server error — quote `request_id` when
    contacting support |
  contact:
    name: Noetive Support
    url: https://noetive.io
    email: support@noetive.io
  license:
    name: Noetive Commercial
    url: https://noetive.io/terms
servers:
  - url: https://semantik.noetive.io
    description: Global Semantik endpoint
security:
  - bearerAuth: []
tags:
  - name: Search
    description: Run a SemQL query against published messages.
  - name: Publish
    description: Ingest messages with embedding vectors.
  - name: Subscribe
    description: Open a live SSE stream of matches for a SemQL query.
  - name: Tooling
    description: Query authoring helpers (lint, auto-complete).
  - name: Health
    description: Liveness probe.
paths:
  /v1/subscribe:
    post:
      tags:
        - Subscribe
      summary: Register a persistent subscription and open an SSE match stream
      description: |
        Compiles the SemQL query, registers a persistent subscription, and
        returns a Server-Sent Events stream of match notifications.

        The stream begins with a `subscribed` event whose `data` is a
        `SubscribedEvent` JSON object. Subsequent `match` events carry a
        `MatchEvent` JSON object. The subscription is removed automatically
        when the client disconnects.

        Example SSE frames:

        ```
        event: subscribed
        data: {"subscription_id":"sub_8f2c..."}

        event: match
        data: {"message_id":"msg_7a21de40f1b2c493","score":0.82}
        ```

        Requires `Content-Type: application/json`.
      operationId: subscribe
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubscribeRequest'
            examples:
              simple:
                summary: Subscribe to a SemQL query
                value:
                  query: MATCH DISTANCE("gpu shortage") WITHIN 0.5
                  namespace: global
                  model: Qwen3-Embedding-4B
                  dimensions: 1024
      responses:
        '200':
          description: |
            SSE stream of match events. `Content-Type: text/event-stream`.

            - First event: `event: subscribed`, data is a `SubscribedEvent`.
            - Subsequent events: `event: match`, data is a `MatchEvent`.
          content:
            text/event-stream:
              schema:
                type: string
                description: |
                  Raw SSE stream. Payload schemas for each event type are
                  `SubscribedEvent` and `MatchEvent`.
        '400':
          description: |
            Invalid request. `error` is one of:

            - `invalid_request` — malformed JSON, an unknown field, a
              missing required field, a SemQL compile or limit error, a
              malformed namespace, or an unknown namespace alias. Also
              returned for a query that constrains nothing (no `DISTANCE`,
              `DIRECTION` or `CONTRAST` clause) and for any use of `TOP`,
              neither of which a subscription supports.
            - `model_not_provisioned` — the namespace alias exists but has
              no entry for the requested (model, dimensions) combination.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Missing or invalid API key bearer token. `error` is `unauthorized`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '402':
          description: |
            Request is authenticated but billing blocks the call. `error` is
            `not_billable`: the account has no active subscription.
            Retrying will keep failing until billing is resolved.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: |
            Namespace is administratively disabled. `error` is
            `namespace_disabled`. Retries do not help until the namespace
            is re-enabled.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          description: >-
            Request body exceeds 1 MB size limit. `error` is
            `request_too_large`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '415':
          description: >-
            Content-Type must be application/json. `error` is
            `unsupported_media_type`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: |
            Rate limited or too many concurrent subscriptions.
            Error codes: `rate_limited` or `too_many_requests`.
            `retry_after_ms` is set; clients should back off.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Subscription setup failed. `error` is `internal_error`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: |
            Service is temporarily unavailable. `error` is one of:

            - `unavailable` — subscription setup exceeded its budget or a
              service dependency is transiently unavailable.
              `retry_after_ms` is set.
            - `namespace_unavailable` — namespace lookup is transiently
              unavailable.
            - `metering_unavailable` — metered billing is transiently
              unavailable. Retry with exponential backoff.
          headers:
            Retry-After:
              description: Seconds to wait before retrying (RFC 9110).
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    SubscribeRequest:
      type: object
      required:
        - query
        - namespace
        - model
        - dimensions
      properties:
        query:
          type: string
          description: |
            SemQL query text for subscription matching. Subject to the SemQL
            query limits — see the "SemQL query limits" section in the API
            description.

            Two clause shapes are rejected here that `/v1/search` accepts:
            a query with no `DISTANCE`, `DIRECTION` or `CONTRAST` clause
            (it would deliver the namespace's entire stream), and any use of
            `TOP` (it ranks within a result set, and a stream has none — use
            `WITHIN` instead). Both return `400 invalid_request`.
        namespace:
          type: string
          maxLength: 64
          pattern: ^[A-Za-z0-9_-]+$
          description: |
            Target namespace alias — a registered name (e.g. `articles`) or
            the well-known `global` alias. Required; there is no default.
            Every request is validated against the namespace's configured
            model and dimensions; a mismatch returns `400 invalid_request`.

            Pass the alias, not the `ns_...` identifier shown in the
            dashboard — a raw identifier is rejected with
            `400 invalid_request`.
        model:
          type: string
          description: |
            Embedding model name. Required; there is no default. Must match
            the model configured on the namespace, or the request fails with
            `400 model_not_provisioned`.
        dimensions:
          type: integer
          minimum: 1
          maximum: 4096
          description: |
            Embedding vector dimensionality. Required; there is no default.
            Must match the dimensionality configured on the namespace for
            `model`, or the request fails with `400 model_not_provisioned`.
    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: |
            Machine-readable error code. See the "Error codes" table in the
            API description for the full list.
        message:
          type: string
          description: Human-readable error description.
        request_id:
          type: string
          description: |
            Server-assigned correlation token, also returned in the
            `X-Request-Id` response header. Quote this when contacting
            support — it pivots directly to the relevant server log line.
        retry_after_ms:
          type: integer
          format: uint32
          description: |
            Hint for how long to wait before retrying. Present on
            `backpressure`, `unavailable`, `namespace_unavailable`, and
            `metering_unavailable` errors; absent (or `0`) means do not
            retry.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: |
        API key bearer token. Obtain from the Noetive dashboard.
        Pass as: `Authorization: Bearer <api-key>`.

````