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

# Create Case Input

> Push raw material on a case (a call transcript, CRM notes, a chat thread, a parsed email as JSON) and let Penbox turn it into case data, the way an inbound email is read.

This is the front door for writing to a case. `data` covers `POST /cases/{id}/data`, `contacts` covers `POST /cases/{id}/contacts`, and `content` is what neither can take. Use the atomic routes when you already know exactly which key to write; use this route when you hold material Penbox should read, or when you want to know what was written **and what was refused**.

At least one of `content`, `data`, `contacts` is required. A call carrying only `data` / `contacts` runs no AI.

- `auto` mode (the default, configurable per case template) writes the values right away, within the case's Email Intelligence *fill* setting: by default an input fills empty fields and never overwrites a value the case already holds.
- `suggest` mode keeps every row as a proposal. Review it with `POST /cases/{id}/inputs/{input_id}/apply`.

Explicit values always win over what the AI reads. Values land on the case in a single write, so automations and Document Intelligence fire as they do for `POST /cases/{id}/data`; Document Intelligence runs after the response, so re-read the case a moment later if a file field with a template was written.

Push raw material on a case and let Penbox read it into case data, the way an inbound email is read.

Use this route when you hold something Penbox should read: a call transcript, CRM notes, a chat thread, a parsed email. Use `POST /cases/{id}/data` when you already know exactly which key to write.

The response is the input resource: what Penbox proposed or wrote, row by row, and what it refused and why. The refreshed case is embedded under `case`.

Contacts sent in `contacts` are the caller's own word: the role is created when the
case does not declare it, an already-bound role is replaced, and the identity fields
you send override the ones on an existing contact. A contact the AI read is more
conservative: it fills a declared role, and only when that role is empty.


## OpenAPI

````yaml POST /cases/{id}/inputs
openapi: 3.0.0
info:
  title: Penbox API
  version: '1.0'
  description: >-
    The Penbox API provides programmatic access to Penbox's form management,
    case management, and document processing capabilities. Authenticate using
    Bearer tokens created at https://app.penbox.io/workspace/settings/api
servers:
  - url: https://connect.penbox.io/v1
    description: Production
security:
  - BearerAuth: []
paths:
  /cases/{id}/inputs:
    post:
      summary: Create Case Input
      description: >-
        Push raw material on a case (a call transcript, CRM notes, a chat
        thread, a parsed email as JSON) and let Penbox turn it into case data,
        the way an inbound email is read.


        This is the front door for writing to a case. `data` covers `POST
        /cases/{id}/data`, `contacts` covers `POST /cases/{id}/contacts`, and
        `content` is what neither can take. Use the atomic routes when you
        already know exactly which key to write; use this route when you hold
        material Penbox should read, or when you want to know what was written
        **and what was refused**.


        At least one of `content`, `data`, `contacts` is required. A call
        carrying only `data` / `contacts` runs no AI.


        - `auto` mode (the default, configurable per case template) writes the
        values right away, within the case's Email Intelligence *fill* setting:
        by default an input fills empty fields and never overwrites a value the
        case already holds.

        - `suggest` mode keeps every row as a proposal. Review it with `POST
        /cases/{id}/inputs/{input_id}/apply`.


        Explicit values always win over what the AI reads. Values land on the
        case in a single write, so automations and Document Intelligence fire as
        they do for `POST /cases/{id}/data`; Document Intelligence runs after
        the response, so re-read the case a moment later if a file field with a
        template was written.
      operationId: create-case-input
      parameters:
        - name: id
          in: path
          required: true
          description: Case UUID
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                context:
                  description: >-
                    What this material is and where it comes from: free text, or
                    any JSON (call id, agent, duration…)
                  oneOf:
                    - type: string
                    - type: object
                  example: >-
                    Transcript of an 11-minute call with the client on
                    2026-09-01
                content:
                  description: >-
                    The material itself: a string (transcript, notes, OCR…) or
                    any JSON value (a parsed email, a webhook payload). Sent to
                    the model as-is. Max 100 000 characters once serialized.
                  oneOf:
                    - type: string
                    - type: object
                data:
                  type: object
                  additionalProperties: true
                  description: >-
                    Explicit values, written as-is. An unknown key lands in the
                    case's extra data, as with `POST /cases/{id}/data`.
                contacts:
                  type: array
                  items:
                    $ref: '#/components/schemas/CaseContactInput'
                  description: Explicit contacts, same shape as `POST /cases/{id}/contacts`
                options:
                  type: object
                  properties:
                    mode:
                      type: string
                      enum:
                        - suggest
                        - auto
                      description: Defaults to the case template setting, itself `auto`
                    fill:
                      type: string
                      enum:
                        - if_empty
                        - always
                        - never
                      description: >-
                        Per-call override of the case fill setting. Governs AI
                        values only.
                    extract_contacts:
                      type: boolean
                      default: true
                      description: >-
                        `false`: the AI proposes no contact; explicit `contacts`
                        are still written
                    suggest_title:
                      type: boolean
                      default: false
                      description: '`true`: the AI proposes a case title as a `title` row'
            example:
              context: Transcript of a call with the client on 2026-09-01
              content: >-
                Client: the water damage happened on 30 August, my policy number
                is POL-123…
              data:
                policy_number: POL-123
              options:
                mode: suggest
      responses:
        '201':
          description: The input resource with its change list, plus the refreshed case
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CaseInput'
        '400':
          description: >-
            Invalid body. `source` is inferred from your token and cannot be
            set; `documents` is not supported yet on this route.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    CaseContactInput:
      type: object
      required:
        - key
        - email
      properties:
        key:
          type: string
          description: >-
            Unique identifier for this contact within the case context. Use
            'main' for the main contact.
        name:
          type: string
          description: The role played by this contact within the case
        email:
          type: string
          format: email
          description: Contact's email address
        given_name:
          type: string
          description: First name
        family_name:
          type: string
          description: Last name
        phone:
          type: string
          description: Contact's phone number
        locale:
          type: string
          description: >-
            Language the contact is addressed in. Defaults to the case locale
            when omitted.
          example: fr
    CaseInput:
      type: object
      properties:
        id:
          type: string
          format: uuid
        created_at:
          type: string
          format: date-time
        case:
          type: object
          properties:
            id:
              type: string
              format: uuid
          description: >-
            The case. On the write routes and on the single read it is the full
            case (same shape as `GET /cases/{id}`).
        source:
          type: string
          description: 'Provenance, inferred from your token: `api`'
          example: api
        type:
          type: string
          nullable: true
          enum:
            - call
            - note
            - chat
            - message
            - document
            - other
          description: >-
            What the material is, derived by Penbox from the context and the
            content
        context:
          nullable: true
          description: What you sent as `context`
        mode:
          type: string
          enum:
            - suggest
            - auto
        status:
          type: string
          enum:
            - proposed
            - partially_applied
            - applied
        intelligence:
          type: object
          properties:
            content:
              type: string
              enum:
                - succeeded
                - failed
                - skipped
              description: '`skipped` when the call carried no `content`'
            error:
              type: string
              nullable: true
        summary:
          type: string
          nullable: true
          description: One paragraph on what the material is about, in the case language
        changes:
          type: array
          items:
            $ref: '#/components/schemas/CaseInputChange'
        unmapped:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
              value:
                type: string
          description: >-
            Facts read in the content that no case field can hold. Never
            written.
        reviewed_at:
          type: string
          format: date-time
          nullable: true
        content:
          nullable: true
          description: >-
            The raw material, verbatim. Only returned by `GET
            /cases/{id}/inputs/{input_id}`.
    Error:
      type: object
      properties:
        errors:
          type: array
          items:
            type: object
            properties:
              status:
                type: string
              title:
                type: string
              detail:
                type: string
              source:
                type: object
                properties:
                  pointer:
                    type: string
    CaseInputChange:
      type: object
      description: One row of the change list. One row, one decision.
      properties:
        id:
          type: string
          description: Stable row id, to pass back to the apply route
          example: c2
        kind:
          type: string
          enum:
            - data
            - contact
            - new_field
            - title
        key:
          type: string
          nullable: true
          description: The case field or contact role. Null on a `title` row.
        label:
          type: string
          nullable: true
        type:
          type: string
          nullable: true
          description: The field type, on `data` and `new_field` rows
        current:
          nullable: true
          description: The value the case held when the input was analysed
        proposed:
          nullable: true
          description: >-
            The value proposed (a string, a number, or a contact object on
            `contact` rows)
        value:
          nullable: true
          description: >-
            What was actually written, once `applied` (the proposal, or the
            reviewer's edit)
        origin:
          type: string
          enum:
            - input
            - ai
          description: '`input`: sent explicitly by the caller. `ai`: read from the content.'
        evidence:
          type: string
          nullable: true
          description: Short verbatim quote supporting an `ai` value
        status:
          type: string
          enum:
            - proposed
            - applied
            - rejected
            - skipped
        reason:
          type: string
          nullable: true
          enum:
            - fill_if_empty
            - fill_never
            - not_extractable
            - create_fields_disabled
            - no_reply_address
            - internal_member
            - main_already_set
            - role_already_set
            - role_not_declared
            - field_exists
            - no_email
            - value_changed
          description: Why a row was `skipped`
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: token
      description: >-
        API token (starts with pnbx_). Create at
        https://app.penbox.io/workspace/settings/api. Include as: Authorization:
        Bearer {token}

````