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

# Get status

> Check whether a connection is available and how a person resolves, before the first read.

## Behavior

When a qualifying connection exists, this read answers an identity or scope refusal as data: a person who cannot be served gets 200 with `scope.refused` set to `identity_unmapped`, `identity_needs_review`, `identity_suspended`, `identity_stale` or `scope_unavailable` instead of a 403. Assertion, service identity and lens refusals still answer with their HTTP status. `has_published: false` means no qualifying connection exists. Send the person the request is for in `X-Nexio-Acting-Principal` and `X-Nexio-Acting-Email`, and the signed `X-Nexio-Records-Assertion` when your organization enforces it. See [Authority and scope](/data-services/scope). See [Preflight with the status read](/data-services/completeness#preflight-with-the-status-read).


## OpenAPI

````yaml GET /api/v1/records/status
openapi: 3.1.0
info:
  title: Nexio API
  version: '1.0'
  description: |
    The Nexio public API. Submit runs to configured engines and read their
    results, hold conversations with configured assistants, make authorized
    reads over the planes and families of a connected system of record, and
    manage webhooks and environments.

    Six engine types are registered: comparison, matching, entity_analysis,
    diligence, triage, and opportunity.

    Every route under /api/v1/ requires `Authorization: Bearer <key>`, except
    inbound event ingest (`POST /api/v1/events/ingest/{source_key}`), which
    authenticates each request against its ingest source instead: an
    HMAC-SHA256 signature for `nexio` and `github` sources, or a shared
    authentication code for an `ams360_ons` source. Error
    bodies share the `Error` schema. Call the API from servers only: it sends
    no CORS headers.
  contact:
    email: support@usenexio.com
    url: https://docs.usenexio.com
servers:
  - url: https://api.usenexio.com
    description: >-
      Nexio API. Sandbox or live is chosen by the API key's environment, not by
      the host.
security:
  - BearerAuth: []
tags:
  - name: EngineManagement
    description: >-
      Create, configure, version and manage engines. An engine is a
      configuration of one registered engine type.
  - name: Engines
    description: Engine-scoped endpoints for submitting runs and retrieving results.
  - name: Runs
    description: Retrieve and reconcile submitted runs.
  - name: Records
    description: >-
      Typed, authorized reads over the registered planes and families of a
      connected system of record, and governed writes to the action ledger. The
      generic family read is the core contract.
  - name: Graph
    description: >-
      The organization's data graph: a read-only map of connections and the
      derivations scheduled on them. Node kinds are a closed registry of 16; the
      API serves 14.
  - name: Environments
    description: >-
      The live environment and up to 5 sandbox environments in your org.
      Organization keys only.
  - name: Webhooks
    description: >-
      Manage webhook endpoints that receive terminal run events (run.completed,
      run.failed, run.cancelled) and run corrections (run.superseded).
  - name: Events
    description: >-
      Send inbound events to your organization's event log through an ingest
      source. Source kinds are nexio, github and ams360_ons; a nexio source
      sends event types from your own vocabulary.
  - name: Converse
    description: The raw stateless conversational model turn (caller-owned state).
  - name: Conversations
    description: >-
      Conversation instances (orchestrators over engines) and their managed
      conversations, turns, annotations, evals, and exports.
  - name: Platform
    description: Service health. No key needed.
paths:
  /api/v1/records/status:
    get:
      tags:
        - Records
      summary: Get status
      description: >-
        Preflight for Records: whether a qualifying connection exists, how the
        acting person resolves, and which reads are available. Unlike every
        other read, when a qualifying connection exists an identity or scope
        refusal (`identity_unmapped`, `identity_needs_review`,
        `identity_suspended`, `identity_stale`, `scope_unavailable`) is answered
        as 200 with `scope.refused` set. A seat that is held for review (no
        single employee record matches) currently answers 500 `internal_error`.
        Assertion and lens refusals keep their HTTP status. With no qualifying
        connection (`has_published: false`), the caller is admitted as on other
        governed routes: a registered service identity
        (`X-Nexio-Service-Identity`, or the API key itself when registered as
        one) is served, and otherwise a request without an acting principal gets
        403 `identity_unmapped` under the shadow and lit postures, and an
        identity refusal answers with its HTTP status.
        `X-Nexio-Service-Identity` is not read when a qualifying connection
        exists.


        Optional diagnostics headers `X-Nexio-Consumer`,
        `X-Nexio-Consumer-Build` and `X-Nexio-Consumer-Vocabulary` are recorded
        and never used for authorization.


        Credential: the organization's live API key, or a scoped key with
        `records:read`. A sandbox or test organization key gets 403
        `scoped_key_required`.
      operationId: getRecordsStatus
      parameters:
        - name: X-Nexio-Acting-Principal
          in: header
          required: false
          description: >-
            The person the request is for, as your identity provider's stable
            user id. Narrows what the key reaches; never widens it. Send it on
            every request; under the shadow and lit postures a read without it
            answers 403 `identity_unmapped`, or 200 with `scope.refused` when a
            qualifying connection exists.
          schema:
            type: string
        - name: X-Nexio-Acting-Email
          in: header
          required: false
          description: >-
            The acting person's verified sign-in email. The seat is derived from
            it.
          schema:
            type: string
        - name: X-Nexio-Records-Assertion
          in: header
          required: false
          description: >-
            Signed assertion `v1.<unix seconds>.<hex HMAC-SHA256>` over
            `acting_principal|acting_email|reserved|timestamp` (each part
            trimmed, the email lowercased). The third field is reserved: send an
            empty string. The timestamp must be within 5 minutes of the server
            clock, either way. Checked once Nexio enables assertion verification
            for your organization, when it provisions the signing secret; a
            missing or wrong assertion then answers 403 `assertion_invalid` and
            one outside the window 403 `assertion_stale`.
          schema:
            type: string
        - name: X-Nexio-Records-Lens
          in: header
          required: false
          description: >-
            `principal:<id>`: a read-only view of another person's data, honored
            only for a caller whose own scope is All or Platform and ignored for
            anyone else. For such a caller, a value that is not
            `principal:<id>`, or names nobody, answers 400
            `lens_target_unknown`.
          schema:
            type: string
        - name: connection_id
          in: query
          required: false
          description: >-
            The system-of-record connection to read. Optional when the
            organization has exactly one qualifying connection; required when it
            has several (otherwise 400 `book_connection_ambiguous`).
          schema:
            type: string
        - name: include_team
          in: query
          required: false
          description: >-
            Add the person's team to `entitlement`. May cost one extra warehouse
            read. Accepts `true`, `false`, `1` or `0`; any other value is 400
            `invalid_request`. Defaults to false.
          schema:
            type: boolean
        - name: include_related_books
          in: query
          required: false
          description: >-
            Include `entitlement.related_books`: counts of the person's own
            records and of each other owner's records the person services or
            shares. Accepts `true`, `false`, `1` or `0`; any other value,
            including an empty one, is 400 `invalid_request`. Defaults to true.
          schema:
            type: boolean
        - name: X-Nexio-Consumer
          in: header
          required: false
          description: Optional client application name, for diagnostics.
          schema:
            type: string
        - name: X-Nexio-Consumer-Build
          in: header
          required: false
          description: Optional client build, for diagnostics.
          schema:
            type: string
        - name: X-Nexio-Consumer-Vocabulary
          in: header
          required: false
          description: >-
            Optional access vocabulary hash the client was built against, for
            diagnostics.
          schema:
            type: string
        - $ref: '#/components/parameters/ServiceIdentityHeader'
      responses:
        '200':
          description: Connection and authority status.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecordsStatusResponse'
          headers:
            Server-Timing:
              description: >-
                Per-read timing: serving resolution (`pin`, no warehouse call on
                a current read), each warehouse statement by name, compose, and
                total.
              schema:
                type: string
            X-Nexio-Engine-Build:
              description: >-
                The engine commit that computed this response, when the build is
                stamped. Key caches on it.
              schema:
                type: string
        '400':
          description: >-
            `invalid_request` (`include_team` or `include_related_books` is not
            a boolean, or `connection_id` is not a UUID),
            `book_connection_ambiguous` when several connections qualify and no
            `connection_id` was sent, or `lens_target_unknown` (a lens that is
            not `principal:<id>` or names nobody).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: >-
            Refused. `scoped_key_required` (a sandbox or test organization key),
            `insufficient_capability` (a scoped key without `records:read`),
            `assertion_invalid` or `assertion_stale`, or
            `lens_caller_unattributed`. With no qualifying connection only: an
            identity refusal (`identity_unmapped`, `identity_needs_review`,
            `identity_suspended`, `identity_stale`, `scope_unavailable`),
            `service_identity_unknown`, or `dataset_denied` from the caller's
            policy.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: >-
            `not_found`: `connection_id` names no qualifying connection in this
            organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: >-
            `book_unavailable`: the acting person's access could not be resolved
            now. `details.reason` names the cause, for example
            `warehouse_unavailable` or `executor_timeout`. Retry. No qualifying
            connection is not a 409 here; it answers 200 with `has_published:
            false`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
        '499':
          $ref: '#/components/responses/ClientClosedRequest'
        '500':
          description: >-
            `internal_error` (including a seat held for review, which has no
            single matching employee record), or `audit_write_failed` when a
            lensed read could not be audited.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          description: >
            `book_unavailable` with `details.reason: busy` when the warehouse
            statement queue is full (retry after `Retry-After` seconds), or
            `resolve_retry_exhausted` when authority resolution kept
            conflicting.


            Also `auth_unavailable`: API key authentication was briefly
            unavailable before the route ran. That cause is transient; retry it
            with backoff.
          headers:
            Retry-After:
              description: Seconds to wait before retrying.
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '504':
          description: '`request_timeout`: the 30 second route timeout expired.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security:
        - BearerAuth: []
components:
  parameters:
    ServiceIdentityHeader:
      name: X-Nexio-Service-Identity
      in: header
      required: false
      description: >-
        A registered service identity, for a machine caller. When sent, it is
        used instead of `X-Nexio-Acting-Principal`. A value that names no active
        service identity answers 403 `service_identity_unknown`.
      schema:
        type: string
  schemas:
    RecordsStatusResponse:
      type: object
      properties:
        has_published:
          type: boolean
          description: >-
            False when no qualifying connection exists; every other read would
            answer 409 `book_unavailable`.
        status:
          type: 'null'
          description: Always null today.
        serving:
          description: >-
            The serving block. Null when no qualifying connection exists
            (`has_published` is false).
          anyOf:
            - $ref: '#/components/schemas/RecordsStatusServing'
            - type: 'null'
        duration_ms:
          type: integer
        read_modes:
          type: object
          additionalProperties:
            type: string
          description: Each read surface and how it is served (`query_first`).
        scope:
          $ref: '#/components/schemas/RecordsStatusScope'
        reads:
          $ref: '#/components/schemas/RecordsStatusReads'
        ams360_base_url:
          type: string
          description: >-
            Base URL for links into the source system, when configured. Omitted
            when not set.
        entitlement:
          type: object
          additionalProperties: true
          description: >-
            The caller's access-plane entitlement. Present when the
            organization's posture is `shadow` or `lit` for this person; with no
            qualifying connection, also for a registered service identity. It
            carries posture, roles, surfaces, datasets, workflows, denied
            fields, documents, grants and record scope, plus `related_books`,
            `team`, `policy_source` and `vocabulary_hash`. Lists and maps are
            always sent, as empty values when there is nothing in them.
        derived:
          $ref: '#/components/schemas/RecordsStatusDerived'
      required:
        - has_published
        - status
        - serving
        - duration_ms
    Error:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: >
            Stable snake_case error identifier. Safe to match programmatically.

            New codes are added over time; treat an unknown code by its HTTP
            status.


            Known codes include:

            `invalid_request`, `invalid_input`, `unauthorized`,

            `auth_unavailable`, `rate_limited`, `missing_run_id`,

            `invalid_run_id`, `run_not_found`, `invalid_offerings`,

            `missing_input`, `queue_unreachable`, `engine_not_found`,

            `engine_slug_conflict`, `engine_archived`, `instance_not_found`,

            `instance_slug_conflict`, `instance_archived`,

            `instance_follows_canonical`, `instance_not_published`,

            `invalid_engine_type`, `validation_error`, `webhook_not_found`,

            `webhook_limit_exceeded`, `invalid_url`, `invalid_events`,

            `invalid_description`, `invalid_auth_token`,

            `missing_endpoint_id`, `invalid_endpoint_id`,

            `missing_delivery_id`, `invalid_delivery_id`,

            `delivery_not_found`, `delivery_not_resendable`,

            `insufficient_capability`, `engine_version_required`,

            `engine_version_exact_required`, `engine_version_not_found`,

            `engine_version_invalid_format`,

            `engine_version_draft_requires_sandbox_key`,

            `engine_version_none_released`, `test_scenario_sandbox_only`,

            `test_scenario_forbidden`, `test_scenario_exact_version_required`,

            `test_scenario_version_not_supported`, `invalid_test_scenario`,

            `request_bound_exceeded`, `run_cap_exceeded`,

            `scoped_key_required`, `engine_binding_forbidden`,

            `request_timeout`, `internal_error`, `idempotency_key_reused`,

            `invalid_idempotency_key`, `acting_principal_mismatch`,

            `run_requires_acting_principal`, `environment_slug_invalid`,

            `environment_slug_reserved`, `environment_slug_taken`,

            `environment_name_invalid`, `environment_limit_reached`,

            `environment_live_immutable`, `environment_not_found`,

            `environment_in_use`, `environment_operation_failed`,

            `list_environments_failed`, `engine_release_unservable`,

            `engine_config_hash_inconsistent`,

            `engine_config_hash_unavailable`,

            `engine_config_version_unavailable`,

            `engine_version_resolve_failed`,

            `engine_config_version_load_failed`,

            `engine_version_publish_mismatch`,

            `engine_version_schema_change_requires_major`,

            `engine_config_hash_missing`, `engine_config_changed`,

            `invalid_engine_config`, `cold_start_gate_not_met`,

            `cold_start_gate_failed`, `cold_start_gate_regressed`,

            `cancel_run_failed`, `event_id_reused`, `invalid_event_id`,

            `missing_event_id`, `invalid_event_type`, `invalid_payload`,

            `reason_text_too_long`, `reason_taxonomy_version_unknown`,

            `invalid_rating`, `missing_comment`, `invalid_target`,

            `invalid_time_on_task`, `scoped_annotation_required`,

            `missing_instance_slug`, `invalid_instance_config`,

            `invalid_eval_waiver`, `instance_config_hash_missing`,

            `config_changed_during_publish`,

            `scenario_set_changed_during_publish`,

            `conversation_eval_regressed`,

            `conversation_eval_execution_failed`,

            `conversation_eval_gate_unavailable`, `publish_failed`,

            `conversation_not_found`, `conversation_archived`,

            `invalid_cursor`, `message_not_found`, `invalid_turn_request`,

            `message_too_long`, `invalid_tool_result`, `invalid_confirmation`,

            `invalid_edit_target`, `turn_in_progress`, `pending_turn`,

            `turn_state_conflict`, `confirmation_environment_unpinned`,

            `instance_config_missing`, `instance_config_invalid`,

            `instance_model_unavailable`, `guardrail_evaluation_failed`,

            `guardrail_config_invalid`, `invalid_conversation_history`,

            `streaming_unsupported`, `converse_unavailable`, `provider_error`,

            `provider_unavailable`, `unknown_model`, `too_many_messages`,

            `too_many_tools`, `invalid_max_tokens`, `invalid_message_role`,

            `invalid_message`, `invalid_content_block`, `invalid_tool`,

            `attachments_not_enabled`, `attachments_unavailable`,

            `attachment_rejected`, `attachment_too_large`,

            `attachments_too_large`, `too_many_attachments`,

            `attachment_not_accepted`, `attachment_not_found`,

            `attachment_changed`, `attachment_read_failed`,

            `attachment_member_delete`, `attachment_not_reservable`,

            `invalid_offset`, `invalid_turn_id`, `invalid_comment`,

            `invalid_reason`, `invalid_feedback_key`, `turn_not_found`,

            `annotation_not_found`, `annotation_not_promotable`,

            `invalid_eval_scenario`, `conversation_eval_scenario_not_found`,

            `conversation_eval_scenario_cap`,

            `conversation_eval_run_not_found`,

            `conversation_eval_baseline_not_found`,

            `conversation_eval_unavailable`,

            `instance_version_invalid_format`, `instance_version_not_found`,

            `document_class_not_open`, `presign_failed`,

            `markets_directory_unavailable`, `book_unavailable`,

            `book_connection_ambiguous`, `identity_unmapped`,

            `identity_needs_review`, `identity_suspended`, `identity_stale`,

            `scope_unavailable`, `assertion_invalid`, `assertion_stale`,

            `cursor_filter_mismatch`, `cursor_expired`,

            `action_schema_unknown`, `action_payload_invalid`,

            `action_out_of_scope`, `action_list_too_large`,

            `overlay_read_only`, `unsupported_node_type`,

            `acting_principal_required`, `action_denied`,

            `appetite_read_denied`, `approval_required`,

            `catalog_access_denied`, `catalog_connection_ambiguous`,

            `create_webhook_failed`, `dataset_denied`, `delivery_id_reused`,

            `document_type_not_servable`, `document_unclassified`,

            `egress_manifest_version_mismatch`,

            `egress_manifest_version_required`, `event_type_not_allowed`,

            `event_type_reserved`, `execution_confirm_required`,

            `execution_prohibited`, `ingest_source_disabled`,

            `ingest_source_misconfigured`, `ingest_source_not_found`,

            `invalid_acting_assertion`, `invalid_active`,

            `invalid_credentials`, `invalid_payload_mode`,

            `invalid_signature`, `load_run_failed`, `load_solutions_failed`,

            `load_work_items_failed`, `missing_engine_slug`, `not_found`,

            `object_store_unconfigured`, `provider_not_approved`,

            `resolve_retry_exhausted`, `run_lookup_failed`,

            `service_identity_unknown`, `stale_timestamp`, `surface_denied`,

            `unresolved_market_question`, `unsupported_market_filter`.


            The full list with causes and fixes is at

            https://docs.usenexio.com/reference/errors.
        message:
          type: string
          description: Human-readable error message. May change between versions.
        details:
          description: |
            Optional request-specific details. Request-bound failures use the
            `RequestBoundDetails` object. Validation failures may use an array
            of field issues or another documented object.

            `409 environment_in_use` is the one exception to this envelope: it
            carries a top-level `blockers` object (see `EnvironmentInUseError`)
            instead of `details`.
    RecordsStatusServing:
      type: object
      properties:
        binding_id:
          type: string
        overlay_rev:
          type: integer
        as_of:
          type: string
          format: date-time
        read_pin:
          type:
            - string
            - 'null'
          format: date-time
          description: Always null on this route.
        source:
          $ref: '#/components/schemas/RecordsReadSource'
        lens:
          type: object
          additionalProperties: false
          required:
            - target
            - display_name
            - scope_kind
          properties:
            target:
              type: string
            display_name:
              type: string
            scope_kind:
              type: string
      required:
        - binding_id
        - overlay_rev
        - as_of
        - read_pin
        - source
    RecordsStatusScope:
      type: object
      properties:
        kind:
          type: string
          description: '`Self`, `Office`, `All` or `Platform`; empty when refused.'
        principal:
          type: string
        refused:
          type: string
          enum:
            - identity_unmapped
            - identity_needs_review
            - identity_suspended
            - identity_stale
            - scope_unavailable
          description: >-
            Set when the person cannot be served. The status read still answers
            200.
        producer_code_count:
          type: integer
        selection:
          type: string
          description: >-
            The selection the request's own `mine`, `book`, `producer`,
            `client_manager` or `account` parameters would serve on a read
            across the whole scope (a label preview; no rows are narrowed).
            Omitted when the scope is refused or those parameters are malformed.
        selection_source:
          type: string
          description: >-
            Who chose `selection` (`request`, `rls` or `none`). Omitted with
            `selection`.
        home_market:
          type:
            - string
            - 'null'
        home_office:
          type:
            - string
            - 'null'
        home_office_label:
          type:
            - string
            - 'null'
        home_status:
          type: string
          enum:
            - ok
            - none
            - unavailable
      required:
        - kind
        - home_market
        - home_office
        - home_office_label
        - home_status
    RecordsStatusReads:
      type: object
      properties:
        account_register:
          allOf:
            - $ref: '#/components/schemas/RecordsReadPath'
          description: >-
            State `available`, `scope_unsupported` or `scope_unavailable`.
            Advisory. The account register route does not check it and serves
            every resolved scope, so its `code` is not a refusal the register
            returns today.
        producer_licensure:
          allOf:
            - $ref: '#/components/schemas/RecordsReadPath'
          description: State `available`, `not_derived` or `version_mismatch`.
      required:
        - account_register
        - producer_licensure
    RecordsStatusDerived:
      type: object
      properties:
        risk_profile:
          $ref: '#/components/schemas/RecordsDerivedSet'
        org_evidence:
          $ref: '#/components/schemas/RecordsDerivedSet'
        org_retention:
          $ref: '#/components/schemas/RecordsDerivedSet'
      required:
        - risk_profile
        - org_evidence
        - org_retention
    RecordsReadSource:
      type: object
      properties:
        mode:
          type: string
          description: >-
            How the connection is served: `query_first`, which means data reads
            go live to the warehouse at request time. On `GET
            /api/v1/records/status` this is a receipt made at request time:
            `current` is `true`, `fetched_at` is the request time, and
            `freshness` is absent.
        current:
          type: boolean
        freshness:
          type: string
          description: Not sent on this route.
        fetched_at:
          type: string
          format: date-time
        stale_since:
          type: string
          format: date-time
      required:
        - mode
        - fetched_at
    RecordsReadPath:
      type: object
      properties:
        state:
          type: string
        code:
          type:
            - string
            - 'null'
          description: The refusal code paired with this state; null when available.
      required:
        - state
        - code
    RecordsDerivedSet:
      type: object
      properties:
        found:
          type: boolean
        batch_set_id:
          type: string
        behind_head:
          type: boolean
        pin_at:
          type: string
          format: date-time
        published_at:
          type: string
          format: date-time
        note:
          type: string
      required:
        - found
        - behind_head
  responses:
    Unauthorized:
      description: Missing or invalid API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: unauthorized
            message: Missing or invalid API key
    RateLimited:
      description: Rate limit exceeded. Retry after the window resets.
      headers:
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: rate_limited
            message: Rate limit exceeded
    ClientClosedRequest:
      description: |
        `client_closed_request`: the caller closed the connection before the
        read finished. Nothing reads this body; it exists so the abandoned
        read is recorded as 499 rather than as a server error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: client_closed_request
            message: The client closed the request before the book read finished
  headers:
    RetryAfter:
      description: Whole seconds to wait before retrying, at least 1.
      schema:
        type: integer
        minimum: 1
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: >
        Send the key as `Authorization: Bearer <key>`. Two kinds of key exist.


        Organization keys are issued in the portal (Settings, then API keys),

        each bound to one environment, shaped `nx_<environment slug>_<64 hex>`.
        They carry

        no capabilities and pass every capability check, with one exception:

        routes under /api/v1/records, /api/v1/engines/{id}/opportunities,

        /api/v1/graph and /api/v1/catalog/documents accept an

        organization key only when its environment is `live`, and refuse any

        other with 403 `scoped_key_required`. Revocation takes effect within 60

        seconds.


        Scoped keys are issued by Nexio on request, shaped

        `nxsk_v1_<24 hex key id>_<43 character secret>`. Each is bound to one

        org, one environment, a set of engines and a set of capabilities. A

        malformed, unknown or revoked `nxsk_` key fails with 401 and is never

        retried as an organization key. Revocation takes effect on the next

        request. A scoped key without a route's capability gets 403

        `insufficient_capability`; a scoped key not bound to the engine gets 403

        `engine_binding_forbidden`.


        Key-grantable capabilities: `engines:read`, `runs:write`, `runs:read`,

        `runs:defensibility:read`, `runs:test`, `catalog:read`,

        `catalog:documents:read`, `webhooks:manage`, `conversations:use`,

        `conversations:export`, `records:read`, `records:opportunities:run`,

        `actions:write`, `actions:read`, `graph:read`, `records:analyze`.


        Routes that accept organization keys only (every scoped key gets 403

        `insufficient_capability`): environment management, engine create,

        update, configuration and publish, and conversation instance

        authoring. Each operation description names the capability a scoped

        key needs.

````