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

# Decrypt payload

> The inverse of encrypt, with the same two modes and the same forwarding
behaviour. An envelope is refused unless it was produced in the
configuration it is presented to, independently of the AEAD check, and
unless the caller holds the clearance the record carries.

Envelopes from the legacy PHP implementation, which begin `qanapi:`, are
refused as a format error rather than attempted and failed.




## OpenAPI

````yaml /openapi/stand-alone/v3.yaml post /api/v3/encryption/{proxy}/decrypt
openapi: 3.1.0
info:
  title: Qanapi Standalone API
  version: 3.0.0
  summary: Single-tenant encryption, key management, classification and audit service.
  description: >
    A standalone cryptographic service. Callers hand it data and it returns

    ciphertext, or hand it ciphertext and it returns data, with every operation

    authorised by policy and recorded on a tamper-evident audit trail.


    ## Authentication


    Two credentials reach the same principal:


    - **API key** in `X-Qanapi-Authorization`, for machine callers. A key
    reaches
      only the configurations it is linked to.
    - **User bearer token** in `Authorization`, from `POST /api/v3/auth/login`,
      for a person working through a console.

    An API key outranks a bearer token when a request carries both, because a

    key names a narrower credential than the user that owns it.


    ## Status codes


    The credential failures are worth stating plainly, because they are not what

    a reader would guess and clients branch on them:


    | Situation | Status |

    |---|---|

    | No credential presented at all | `400` |

    | A credential that does not verify | `401` |

    | A revoked API key | `403` |

    | Authenticated but not permitted | `403` |

    | Request body or parameter is invalid | `422` |


    All of these match the Laravel v3 API this service replaces. Every error
    body

    carries `message` and `error`; validation failures additionally carry a

    field-keyed `errors` object.


    ## Rate limiting


    Every response carries `X-RateLimit-Limit` and `X-RateLimit-Remaining`. The

    window is per node and keyed on the client address, so a cluster of n nodes

    admits n times the configured figure.
  license:
    name: Proprietary
    identifier: LicenseRef-Qanapi-Commercial
servers:
  - url: https://{host}
    description: A deployed instance, behind the reverse proxy that terminates TLS.
    variables:
      host:
        default: qanapi.example.com
  - url: http://localhost:8000
    description: The local Compose cluster, through its load balancer.
security:
  - ApiKeyAuth: []
  - BearerAuth: []
tags:
  - name: STAND
    description: |
      Every operation this service serves. Carried on all of them so the
      standalone surface can be selected as a whole, which matters where this
      description is rendered or merged alongside another Qanapi API.

      It is a marker rather than a grouping: each operation keeps the tag that
      says what it does, and that one is listed first.
  - name: Health
    description: |
      Liveness and readiness probes. Registered outside the authentication,
      audit and transport layers, so a load balancer polling them without
      credentials still gets an answer.
  - name: Authentication
    description: Logging in, and creating the first administrator.
  - name: Users
    description: User accounts and their roles.
  - name: API keys
    description: >
      Machine credentials. A key is scoped to a set of configurations and
      reaches

      nothing until it is linked to at least one.
  - name: Configurations
    description: >
      Encryption configurations, called configurations on the wire for
      compatibility

      with the v3 API this service replaces. A configuration owns a master key
      and is

      the unit of cryptographic isolation.
  - name: Classifications
    description: |
      Sensitivity labels and the clearance that reaches them.
  - name: Policies
    description: Allow and deny rules binding a principal to actions on resources.
  - name: Encryption
    description: >
      The data plane. Field-level or whole-body encryption under a
      configuration,

      optionally forwarded onward to a destination of the caller's choosing.
  - name: KMS
    description: Customer-managed key lifecycle, import and export.
  - name: Audit
    description: The tamper-evident record of everything the service did.
  - name: Transport
    description: The service's own key pair, for end-to-end encrypted request bodies.
paths:
  /api/v3/encryption/{proxy}/decrypt:
    parameters:
      - $ref: '#/components/parameters/ProxySlug'
    post:
      tags:
        - Encryption
        - STAND
      summary: Decrypt payload
      description: |
        The inverse of encrypt, with the same two modes and the same forwarding
        behaviour. An envelope is refused unless it was produced in the
        configuration it is presented to, independently of the AEAD check, and
        unless the caller holds the clearance the record carries.

        Envelopes from the legacy PHP implementation, which begin `qanapi:`, are
        refused as a format error rather than attempted and failed.
      operationId: decrypt
      parameters:
        - $ref: '#/components/parameters/Fields'
        - $ref: '#/components/parameters/FullBody'
        - $ref: '#/components/parameters/Mode'
        - $ref: '#/components/parameters/Destination'
      requestBody:
        required: true
        content:
          application/json:
            example:
              name: Jane Doe
              ssn: QANAPI-RS:v1:MDFhMDg2ZGQ:Vpp6gZK4ZRUwgvpcnA:$
          text/plain:
            schema:
              type: string
      responses:
        '200':
          description: The plaintext, in the shape it was encrypted from.
          headers:
            X-Qanapi-Action-Count:
              $ref: '#/components/headers/ActionCount'
            X-Qanapi-Forwarded-To:
              $ref: '#/components/headers/ForwardedTo'
          content:
            application/json:
              example:
                name: Jane Doe
                ssn: 123-45-6789
            text/plain:
              schema:
                type: string
        '400':
          description: |
            No credential, or the envelope could not be read. Every
            cryptographic failure answers identically, so neither the status nor
            the body says whether a padding check, an authentication tag or a
            key lookup was what failed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/BadCredential'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/ProxyNotReachable'
        '422':
          $ref: '#/components/responses/ValidationFailed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
components:
  parameters:
    ProxySlug:
      name: proxy
      in: path
      required: true
      description: >
        The configuration's proxy slug. Sixteen random lowercase characters
        unless

        one was pinned at creation.
      schema:
        type: string
        examples:
          - 952zpx6vyig3sdyp
    Fields:
      name: X-Qanapi-Fields
      in: header
      description: |
        Comma-separated dot paths to encrypt or decrypt, for example
        `ssn,user.address.postcode`. Absent in whole-body mode.
      schema:
        type: string
        examples:
          - ssn,user.dob
    FullBody:
      name: X-Qanapi-Full-Body
      in: header
      description: Treat the whole body as one payload. `true`, `1`, `yes` or `on`.
      schema:
        type: string
        examples:
          - 'true'
    Mode:
      name: X-Qanapi-Mode
      in: header
      description: Alternative spelling of whole-body mode; `full_body`.
      schema:
        type: string
        enum:
          - full_body
          - full-body
    Destination:
      name: X-Qanapi-Destination
      in: header
      description: |
        Forward the processed payload to this absolute URL and relay the answer
        back. Every request header travels except `x-qanapi-*`, `authorization`
        and the hop-by-hop set. Restricted by `FORWARD_ALLOWED_HOSTS` where the
        installation sets it.
      schema:
        type: string
        format: uri
  headers:
    ActionCount:
      description: How many cryptographic operations the request performed.
      schema:
        type: integer
    ForwardedTo:
      description: >
        Present only when the response was relayed from a destination, naming

        it, so a caller can tell a proxied answer from one this service
        produced.
      schema:
        type: string
        format: uri
    RateLimitLimit:
      description: Requests allowed per minute, per node.
      schema:
        type: integer
    RateLimitRemaining:
      description: Requests left in the current window.
      schema:
        type: integer
  schemas:
    Error:
      type: object
      required:
        - message
        - error
      properties:
        message:
          type: string
          description: >
            Human readable. A 5xx is described by its category and nothing more:

            operational detail such as a path or a setting name stays in the
            log.
          examples:
            - Unauthorized.
        error:
          type: string
          description: |
            A stable machine readable code. This service sends it and the v3 API
            it replaces does not, which is what lets the two share the same
            `message` strings without losing specificity: `Resource not found.`
            still arrives alongside `KmsKeyNotFoundException`.
          examples:
            - InvalidApiKeyException
    ValidationError:
      allOf:
        - $ref: '#/components/schemas/Error'
        - type: object
          required:
            - errors
          properties:
            errors:
              type: object
              description: >
                Messages keyed by the field they concern. A failure with no
                single

                field to blame is filed under `request`.
              additionalProperties:
                type: array
                items:
                  type: string
              examples:
                - type:
                    - The type field is required.
                  configuration_ids:
                    - No configuration has the id `...`.
  responses:
    BadCredential:
      description: A credential was presented and did not verify.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: |
        Authenticated, but not permitted. Also returned for a revoked API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    ProxyNotReachable:
      description: >
        Reported as a missing API key. The answer is deliberately the same

        whether no configuration has that slug or the presented key is not
        linked to

        the one that does, so an unrelated key cannot enumerate configurations.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            message: API key not found
            error: ApiKeyNotFoundException
    ValidationFailed:
      description: |
        The request body or a parameter is invalid. `message` is always
        `Validation failed`; the specifics are in `errors`, keyed by field.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ValidationError'
    TooManyRequests:
      description: The rate limit for this client address was exhausted.
      headers:
        X-RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-Qanapi-Authorization
      description: >
        A machine credential, `qk_` followed by its secret. The first twelve

        characters are an indexed prefix; the rest is compared in constant time

        against a stored SHA-256 hash. A key reaches only the configurations it
        is

        linked to.


        It may also be sent as `Authorization: Bearer qk_...`, which is

        recognised by the prefix.
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: |
        A user token from `POST /api/v3/auth/login`. The token asserts a role,
        but the database decides it, so a role changed or a user deleted after
        the token was issued takes effect immediately. A token minted before a
        password change is refused.

````