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

# Get a dataset's sample redaction

> Return the stored redaction for a dataset's sample: which columns hold personal data, and how
each is masked. Apply it with `GET /datasets/{dataset_id}/sample/redacted`.

A redaction gets here one of two ways: you upload one with `PUT /datasets/{dataset_id}/redaction`,
or Narrative's privacy model writes one. `POST /v2/datasets/{dataset_id}/request-sample` asks the
model for one in a single call, and the `GenerateSampleRedaction` workflow task asks for the same
thing as a step in a workflow of your own. `model_version` tells an uploaded redaction from a
generated one.



## OpenAPI

````yaml https://docs-cdn.narrative.io/api-reference/main/openapi.json get /datasets/{dataset_id}/redaction
openapi: 3.1.1
info:
  contact:
    email: support@narrative.io
    name: Narrative Support
    url: https://www.narrative.io
  termsOfService: https://www.narrative.io/legal/terms-of-service
  x-logo:
    url: >-
      https://cdn.narrative.io/images/company-logos/prod/narrative-logo-text-white.svg
    backgroundColor: rgb(9, 34, 166)
    altText: Narrative Logo
  description: >-
    The [Narrative Data Collaboration Platform](https://app.narrative.io) API is
    organized around REST. Our API has predictable resource-oriented URLs,
    accepts form-encoded request bodies, returns JSON-encoded responses, and
    uses standard HTTP response codes, authentication, and verbs.



    The current version is a pre-release beta.  It may result in unexpected
    behavior and there may be breaking changes in future releases up to the 1.0
    release.
  title: Narrative Data Collaboration Platform API
  version: 2.4.x
servers:
  - url: https://api-dev.narrative.io
  - url: https://api.narrative.io
security: []
tags:
  - name: Access Rules
    description: >-
      Access rules let data providers control who can purchase their data and
      the terms of purchase.


      The `access-rules` API allows you to manage access rules for your
      datasets.


      Related guides:
        - [What is an access rule?](https://kb.narrative.io/access-rules)
  - name: Agent Conversations
    description: >-
      Build LLM-driven workflows that can call MCP tools, ask the caller for
      input, and return structured answers.


      A conversation pins a model, a system prompt, and a tool catalog. Each run
      sends the model a user message (or

      a batch of tool outputs from a previously-paused run); the model decides
      whether to answer directly, call a

      server-side tool (resolved by the platform via Model Context Protocol), or
      call a client-side tool (which pauses

      the run with `requires_action` and waits for the caller to reply).


      Related guides:
        - [Agent Conversations Reference](https://docs.narrative.io/reference/architecture/agent-conversations)
        - [Error catalog](https://docs.narrative.io/reference/architecture/agent-conversations/errors/conversation-not-found)
  - name: MCP Connections
    description: >-
      Connect external (non-Narrative) MCP servers so agent runs can call their
      tools with the

      calling user's own OAuth authorization.


      A connection is created interactively: `POST /mcp-connections` runs OAuth
      discovery and

      Dynamic Client Registration against the server and returns a consent URL;
      the user authorizes

      in a browser; the authorization server redirects to `GET
      /mcp-connections/callback`, which

      stores the authorization code and sends the browser on to the Narrative
      app; the app calls

      `POST /mcp-connections/complete` with the user's own bearer token, which
      exchanges the code and

      marks the connection `connected`.


      That last step is authenticated on purpose. The callback is public — it's
      a cross-site browser

      redirect, so no bearer reaches it — so it is not allowed to complete
      anything. Only the user

      who started the flow can turn the stored code into a token.


      Tokens are stored encrypted and used server-side — they are never returned
      by the API. Once

      connected, reference the connection by id from an agent run's
      `mcp_servers[].connection_id`.
  - name: App Invites
    description: >-
      App invites allow applications to create one-time, shareable links on
      behalf of their users. These links

      enable users to invite third parties who do not have a Narrative account
      to perform actions within the

      application.


      For example, a Narrative user can send an invite link to a third party who
      then completes a Pinterest

      OAuth flow and creates a connector profile in the inviter's account,
      allowing the inviter to deliver

      audience data to the third party's Pinterest account without the third
      party needing a Narrative account.


      This API is intended to be used by applications (via app client
      credentials) to create and manage invites

      on behalf of their users, which can then be shared with invitees.
  - name: Apps
    description: >-
      Apps are applications bundled with a UI that can perform various actions
      on behalf of a user utilizing the Narrative API.

      Related guides:
        - [Building a Narrative Native App](https://www.narrative.io/knowledge-base/how-to-guides/building-a-narrative-native-app)
  - name: Access Tokens
    description: >-
      System Access Tokens are long-lived bearer credentials for calling the API
      without a user present.


      A token belongs to a company, carries an explicit list of permissions, and
      expires on a date you choose — at

      least a day out and at most a year. `admin` access cannot be granted to
      one.


      `POST /access-tokens/tokens` is the only endpoint that returns the
      credential. Everything after that works in

      terms of the token's id: the list, get and update responses describe a
      token without ever repeating its

      `access_token`. If you lose it, delete the token and create another.


      Related guides:
        - [How to Create an API Token](https://www.narrative.io/knowledge-base/how-to-guides/understanding-narratives-apis/create-an-api-token)
  - name: Attributes
    description: >-
      An attribute models a standardized data point available for sale on the
      Narrative marketplace.


      Narrative automatically turns data points from provider datasets into
      attributes so that buyers can purchase well-formed, standardized data from
      any supplier on the marketplace.
  - name: Auth
    description: >-
      API token is a crucial step for developers to securely authenticate
      requests to the Narrative API

      Related guides:
        - [How to Create an API Token](https://www.narrative.io/knowledge-base/how-to-guides/understanding-narratives-apis/create-an-api-token)
  - name: Authentication
    description: User login and registration
  - name: Billings
    description: Used by Narrative internally to bill customers
  - name: Companies
    description: A collection of employees
  - name: Company Marketing Information
    description: Useful information related to companies
  - name: Compute Pools
    description: >-
      Compute pools represent compute resources (e.g. Snowflake warehouses)
      provisioned within a data plane.

      Companies can manage and share compute pools, assign them to jobs, and set
      a default compute pool on data planes.
  - name: Connections
    description: Associations between connectors and datasets
  - name: Data Shops
    description: |-
      Self-hosted website to sell your data
      Related guides:
        - [Setting up your datashop](https://www.narrative.io/knowledge-base/how-to-guides/shop-builder/settting-up-your-data-shop)
  - name: Data Streams
    description: >-
      The `data-stream` API endpoints allows one to create and update
      data-streams. Additionally the endpoints allow

      finding data-streams using free text search. A few of the endpoints are
      behind authorization.


      Update endpoint allows a client to post an edited data-stream document as
      is, without having to change its shape.

      The API ensures that only certain fields are allowed to be modified.
      Attempts to modify fields not up for client

      modifications are ignored.


      Related guides:
        - [What is a data stream?](https://kb.narrative.io/what-is-a-data-stream)
  - name: Contracts
    description: Contracts related APIs
  - name: Datasets
    description: >-
      Any kind of data, in any schema, can be pushed into the Narrative Data
      Collaboration Platform as a dataset exactly as it is stored in your own
      system.


      The `datasets` API allows you to manage your datasets.
  - name: Derivations
    description: >-
      Derivation rules describe how the value of one Rosetta Stone attribute can
      be computed from another — for

      example, hashing a raw email into a SHA-256 hashed email. Each rule
      carries a transformation expression that

      reads the source attribute's value through `$source`.


      The `derivations` API allows you to browse the rules your company can use
      — the ones it owns plus the ones

      shared with it through collaborators. Creating and changing them requires
      an `admin` grant on

      `attribute_derivations`, and a rule can only be changed by the company
      that owns it.
  - name: Destinations
    description: >-
      Destinations associate a subscription to a profile. Optionally, ad-hoc
      quick settings can be configured to a destination.

      Those quick settings have to match the format defined on the app manifest.
  - name: Installations
    description: Installations of Applications for a profile
  - name: Jobs
    description: >-
      Jobs represent an operation done on a given data plane. All jobs today are
      tied to a query that represents a forecast or a materialized view.


      The jobs API provides an interface for interacting with the jobs table,
      which stores various operations involving reading or writing data. This
      API allows users to retrieve detailed information about specific jobs,
      including NQL forecasts and materialized views.
  - name: Mappings
    description: >-
      A mapping is a transformation from a dataset to an attribute. Defining a
      mapping between a dataset and an attribute makes the dataset eligible to
      participate in subscriptions where a buyer is purchasing the target
      attribute.
  - name: Model Inference
    description: Model Inference
  - name: Model Training
    description: >-
      Train machine learning models on datasets, e.g. text classifiers used to
      power attribute mappings.
  - name: Models
    description: >-
      Machine learning models for training and inference.


      Models can be stored in HuggingFace or Narrative repositories and have
      configurable

      collaborator permissions for training and inference access.


      The `models` API allows you to list, retrieve, and update models
      accessible to your company.
  - name: NQL
    description: >-
      Narrative Query Language (NQL) is a specialized, SQL-inspired language
      designed to query and manipulate data within the Narrative platform. While
      it looks and feels much like standard SQL, it offers extended
      functionality and syntax that let you leverage platform-specific
      features—such as referencing datasets by their IDs, creating materialized
      views, or generating forecasts—without having to manage the complexities
      of different query engines behind the scenes. NQL queries can ultimately
      compile down to multiple underlying engines (e.g., Snowflake, Spark) to
      execute your requests efficiently in the Narrative ecosystem.
  - name: Payment Methods
    description: Payment methods used to purchase data
  - name: Products
    description: Internal routes used to offer datastream as products
  - name: Profiles
    description: >-
      App profiles are associated with an installation. They represent a
      reference to a configuration that the app can use to save confidential
      information outside of Narrative's control.

      Profiles are currently used to configure settings for connector apps.
  - name: Resources
    description: >-
      Narrative gives you access to managed resources, like your own AWS S3
      bucket, so that you can effortlessly buy and sell data on the platform.


      The `resources` API allows you to manage your resources.
  - name: Schema Inference
    description: >-
      The `schema-inference` API analyzes submitted files to automatically infer
      and return their structure as a dataset schema.
  - name: Schema Presets
    description: >-
      The `schema-presets` API allows you to list the available schema presets,
      get detailed information about a specific one and manage its life cycle.


      You can create a schema preset from scratch or create one based on an
      existing one, administrators can create platform wide available (public)
      schema preset.
  - name: Subscriptions
    description: >-
      In the Narrative Data Collaboration Platform a subscription represents a
      set of rules dictating the commercial terms related to the licensing of
      data.


      The `subscriptions` API allows you to set and get information about
      `subscription` objects owned by the authenticated account.
  - name: Uploads
    description: >-
      The `uploads` API allows you to send files to Narrative and use them to
      perform tasks like creating a list or adding data to a dataset.
  - name: Usage
    description: >-
      The `usage` API enables the recording of usage events associated with a
      product.
  - name: Webhooks
    description: >-
      Webhooks push events to a URL you control instead of making you poll for
      them.


      You create a subscription with the endpoint you want events sent to and a
      filter describing which events you

      want. Two kinds of subscription exist: job subscriptions follow the
      lifecycle of Narrative jobs, and app

      subscriptions follow the events a Narrative app reports through `POST
      /apps/events`.


      Creating a subscription returns a `secret`. Narrative sends it back in the
      `X-Narrative-Secret` header on

      every delivery, and comparing the two is how you tell a real callback from
      a forged one — so store it and do

      not share it.


      Deliveries are described under the "Event delivery" webhook below. Return
      any 2xx; anything else is retried

      with exponential backoff, and because a retry reuses the envelope's `id`,
      handlers should be idempotent

      on it.
  - name: Workflows
    description: >-
      The `workflows` API allows you to create, schedule, trigger, and archive
      workflows.

      Workflows are defined using a serverlessworkflow YAML specification.
paths:
  /datasets/{dataset_id}/redaction:
    get:
      tags:
        - Dataset Samples
      summary: Get a dataset's sample redaction
      description: >-
        Return the stored redaction for a dataset's sample: which columns hold
        personal data, and how

        each is masked. Apply it with `GET
        /datasets/{dataset_id}/sample/redacted`.


        A redaction gets here one of two ways: you upload one with `PUT
        /datasets/{dataset_id}/redaction`,

        or Narrative's privacy model writes one. `POST
        /v2/datasets/{dataset_id}/request-sample` asks the

        model for one in a single call, and the `GenerateSampleRedaction`
        workflow task asks for the same

        thing as a step in a workflow of your own. `model_version` tells an
        uploaded redaction from a

        generated one.
      parameters:
        - $ref: '#/components/parameters/parameters_DatasetId-2'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SampleRedaction'
        '401':
          description: >-
            Authentication token is malformed, invalid or insufficient
            permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DataStream_Error'
        '403':
          description: >-
            Raised when the token used to authenticate the request is not
            associated with a company.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DataStream_Error'
        '404':
          description: >-
            Raised when the dataset doesn't exist, isn't owned by the auth
            token's company, or has no

            redaction stored. Upload one via `PUT
            /datasets/{dataset_id}/redaction`, or ask the privacy

            model for one via `POST /v2/datasets/{dataset_id}/request-sample`.


            A workflow run that failed also looks like this, because nothing
            records the failure against

            the dataset. Check the workflow run if a redaction you asked for
            never appeared.
        '500':
          description: >-
            Raised when the stored redaction can't be read: an unrecognised
            `masking_strategy`, a

            `category` that isn't one of the nine supported categories, or
            `columns` or `written_by` that

            don't decode at all. What this endpoint returns is always what was
            written.


            `GET /datasets/{dataset_id}/sample/redacted` answers 500 for the
            same redaction, rather than

            serving rows masked by a strategy nobody chose.


            The body is an RFC 7807 problem document. `title` is `Stored
            Redaction Unreadable`, `status`

            is `500`, `instance` is `/redaction`, and `detail` is always

            `The dataset's stored redaction could not be read. Provide the
            log_id for support.` `type`,

            `debug`, and `app` are null.


            Which column failed to read, and why, goes to the server logs rather
            than the body. Support

            can find that log line by `log_id`, so quote it when you report the
            failure.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Response'
      security:
        - BearerAuth: []
components:
  parameters:
    parameters_DatasetId-2:
      in: path
      name: dataset_id
      required: true
      description: Unique identifier for a dataset.
      schema:
        type: integer
  schemas:
    SampleRedaction:
      description: The stored redaction for one dataset's sample.
      type: object
      required:
        - dataset_id
        - columns
        - written_by
        - model_version
        - created_at
        - updated_at
      properties:
        dataset_id:
          description: The dataset this redaction belongs to.
          type: integer
        columns:
          description: One entry per column that holds personal data.
          type: array
          items:
            $ref: '#/components/schemas/RedactedColumn'
        written_by:
          $ref: '#/components/schemas/RedactionWriter'
        model_version:
          description: >-
            The privacy model that produced the redaction, named by the model
            repository the worker

            loaded. A deployment can point the worker at a different repository,
            so read the value rather

            than expecting a fixed one.


            Null on a redaction you uploaded yourself: `PUT
            /datasets/{dataset_id}/redaction` refuses this

            field from any caller but Narrative, and an upload that leaves it
            out clears a stored value.
          type:
            - string
            - 'null'
          maxLength: 128
        created_at:
          description: >-
            When the dataset's first redaction was written. Survives later
            uploads.
          type: string
          format: date-time
        updated_at:
          description: When the redaction was last written or deleted.
          type: string
          format: date-time
      example:
        dataset_id: 1234
        columns:
          - path: email_address
            category: private_email
            masking_strategy:
              type: keep_email_domain
          - path: payment.card_number
            category: account_number
            masking_strategy:
              type: keep_last_digits
              count: 4
        written_by:
          type: app_installation
          user_id: null
          company_id: 42
          app_id: 8
          installation_id: 17
        model_version: openai/privacy-filter
        created_at: '2026-07-30T18:04:11Z'
        updated_at: '2026-07-30T18:04:11Z'
    DataStream_Error:
      type: object
      properties:
        error:
          description: error code of the error
          type: string
        error_description:
          description: human friendly error text
          type: string
      required:
        - error
        - error_description
    Response:
      type: object
      required:
        - title
        - status
        - detail
        - instance
        - log_id
      properties:
        type:
          description: A URL naming the class of error.
          type:
            - string
            - 'null'
          format: uri
        title:
          type: string
          description: a summary of the error
        status:
          type: integer
          description: http status code
        detail:
          type: string
          description: a longer description of the error
        instance:
          type: string
          description: path of the query
        log_id:
          type: string
          format: uuid
          description: an internal logging id, Narrative can use to help debug a problem.
      example:
        type: null
        title: Failed to Parse Query
        status: 400
        detail: Invalid query format.
        instance: /nql/validate
        log_id: c06fea02-0950-45c6-aaff-07ab07c0d04c
        debug: null
    RedactedColumn:
      description: >-
        One column of the sample, the kind of personal data it holds, and how it
        is masked. The category

        does not constrain the strategy: a redaction you upload can pair any
        category with any strategy.
      type: object
      required:
        - path
        - masking_strategy
      properties:
        path:
          description: >-
            Dot-separated path to the column in the sample, e.g. `email_address`
            or

            `payment.card_number`. A path that doesn't resolve in a row leaves
            that row untouched, so a

            redaction naming a since-renamed column quietly stops masking it.
          type: string
          example: email_address
        category:
          oneOf:
            - $ref: '#/components/schemas/PersonalDataCategory'
            - type: 'null'
          description: >-
            Optional on a redaction you upload: send `null` or leave the key out
            for a column you don't

            want to classify. The privacy model sets it on the redactions it
            writes. Nothing masks by the

            category, only by `masking_strategy`, which is why the category may
            be left out and the

            strategy may not. A stored redaction always returns the key, `null`
            for a column with no

            category.
        masking_strategy:
          $ref: '#/components/schemas/MaskingStrategy'
      example:
        path: email_address
        category: private_email
        masking_strategy:
          type: keep_email_domain
    RedactionWriter:
      description: >-
        Who was authenticated when the redaction was written, taken from the
        access token rather than

        from anything the caller sent. One variant per kind of grant a token can
        carry, each tagged with

        `type`:


        - `user_resource_owner`: a token issued directly to a user of a company.

        - `app_installation`: a token issued to an app a company installed.
        `user_id` is null when the
          app issued itself the token rather than acting for a user.
        - `app_client_credentials`: a token issued to an app on its own
        credentials, which carries no
          user and no company.
      oneOf:
        - title: UserResourceOwner
          type: object
          required:
            - type
            - user_id
            - company_id
          properties:
            type:
              type: string
              enum:
                - user_resource_owner
            user_id:
              description: The user the access token was issued to.
              type: integer
            company_id:
              description: The company that user belongs to.
              type: integer
          example:
            type: user_resource_owner
            user_id: 4711
            company_id: 42
        - title: AppInstallation
          type: object
          required:
            - type
            - user_id
            - company_id
            - app_id
            - installation_id
          properties:
            type:
              type: string
              enum:
                - app_installation
            user_id:
              description: >-
                The user the app was acting for, or null when the app issued
                itself the token.
              type:
                - integer
                - 'null'
            company_id:
              description: The company that installed the app.
              type: integer
            app_id:
              description: The installed app.
              type: integer
            installation_id:
              description: That app's installation the token was issued against.
              type: integer
          example:
            type: app_installation
            user_id: null
            company_id: 42
            app_id: 8
            installation_id: 17
        - title: AppClientCredentials
          type: object
          required:
            - type
            - app_id
          properties:
            type:
              type: string
              enum:
                - app_client_credentials
            app_id:
              description: The app the access token was issued to.
              type: integer
          example:
            type: app_client_credentials
            app_id: 8
      discriminator:
        propertyName: type
      example:
        type: user_resource_owner
        user_id: 4711
        company_id: 42
    PersonalDataCategory:
      description: >-
        The kind of personal data found in a column. Nine values are accepted:


        - `account_number`

        - `other`

        - `private_address`

        - `private_date`

        - `private_email`

        - `private_person`

        - `private_phone`

        - `private_url`

        - `secret`


        The privacy model produces every one of them except `other`, which is
        for a redaction you upload

        yourself when none of the other eight fit.


        On write, a value outside that list is rejected with a 400 that names
        the value it refused and

        lists all nine that are accepted.


        Both read endpoints answer 500 for a stored `category` this API can't
        read, rather than

        reporting or masking by something nobody wrote.
      type: string
      enum:
        - account_number
        - other
        - private_address
        - private_date
        - private_email
        - private_person
        - private_phone
        - private_url
        - secret
      example: private_email
    MaskingStrategy:
      description: >-
        How a column's value is masked. Seven variants, each a JSON object
        tagged with `type`:


        - `keep_email_domain`: keeps the domain, plus the local part's first and
        last two characters
          (or just its first character when the local part is two to four characters long, and nothing
          at all when it is one character). A value with no `@`, or one that starts with `@` and so has
          no local part, is masked completely, domain included: `@example.com` masks to `************`.
        - `keep_initials`: replaces each whitespace-separated token with its
        first letter and three mask
          characters, whatever the token's length, so `John Doe` becomes `J*** D***`.
        - `keep_network_prefix`: keeps an IPv4 address's `/24` and masks the
        rest. Anything else, IPv6
          included, is masked completely.
        - `keep_two_decimal_places`: rounds a coordinate to two decimal places.
        A value that isn't a
          coordinate is masked completely.
        - `keep_year`: returns the first four-digit run in the value and drops
        the rest, so `1985-03-12`
          becomes `1985`. A value with no four-digit run is masked completely.
        - `keep_last_digits`: keeps the last `count` digits, masks every other
        letter and digit, and
          leaves separators alone so the value's shape survives. When the value has no other letter or
          digit to mask it keeps one digit fewer, so `1234` with `count: 4` masks to `*234`.
        - `mask_everything`: keeps nothing.


        On write, an unrecognised `type` is rejected with a 400 and nothing is
        stored. The 400 names the

        column it refused, the value it didn't recognise, and all seven types it
        accepts. Accepting the

        typo and masking some other way instead would be worse than refusing:
        someone who typed

        `keep_emial_domain` would believe the domain was being kept, see
        fully-masked values, and get

        nothing telling them the strategy they asked for wasn't the one in use.


        Both read endpoints answer 500 for a stored `masking_strategy` this API
        can't read, rather than

        naming a strategy nobody wrote or masking by one. That 500 carries a
        `log_id` and not the column

        that failed, so quote it when you report the failure.
      oneOf:
        - title: KeepEmailDomain
          type: object
          required:
            - type
          properties:
            type:
              type: string
              enum:
                - keep_email_domain
          example:
            type: keep_email_domain
        - title: KeepInitials
          type: object
          required:
            - type
          properties:
            type:
              type: string
              enum:
                - keep_initials
          example:
            type: keep_initials
        - title: KeepNetworkPrefix
          type: object
          required:
            - type
          properties:
            type:
              type: string
              enum:
                - keep_network_prefix
          example:
            type: keep_network_prefix
        - title: KeepTwoDecimalPlaces
          type: object
          required:
            - type
          properties:
            type:
              type: string
              enum:
                - keep_two_decimal_places
          example:
            type: keep_two_decimal_places
        - title: KeepYear
          type: object
          required:
            - type
          properties:
            type:
              type: string
              enum:
                - keep_year
          example:
            type: keep_year
        - title: MaskEverything
          type: object
          required:
            - type
          properties:
            type:
              type: string
              enum:
                - mask_everything
          example:
            type: mask_everything
        - title: KeepLastDigits
          type: object
          required:
            - type
            - count
          properties:
            type:
              type: string
              enum:
                - keep_last_digits
            count:
              description: How many trailing digits survive. PCI practice keeps 4.
              type: integer
              minimum: 1
              maximum: 4
          example:
            type: keep_last_digits
            count: 4
      discriminator:
        propertyName: type
      example:
        type: keep_email_domain
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer

````