Skip to main content
POST
Create Case Input
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.

New fields

The AI may add new fields to the case for information that matches no existing field. This is controlled by options.create_fields. It defaults to the case setting, then true. Send false to keep that information in unmapped instead. This is how you fill a case created without a template. Post one input per message and the case builds up as the conversation goes.

Documents

Send files in documents, 5 at most per call. Each file is either an attachment you already uploaded, or an inline base64 file.
Two ways to send a file
The whole request body must stay under 50 MB. For a big file, upload it first with POST /attachments, then send its id here. That route takes files up to 10 GB. Penbox reads each file and files it on the right file field of the case. This classification happens during the request. The response tells you where every document went. Add a key to a document to file it on that field yourself. The classifier is then skipped for that file. The key must be a file field of the case, else the call is a 400 with the code not_file_field.
Filed on an explicit field
Set options.classify_documents to false to switch the classifier off. Documents without a key then come back with the status unclassified. The file is uploaded and kept on the workspace, but it is not filed on the case. You can file it later with POST /cases/{id}/inputs/{input_id}/apply. intelligence.documents says how the classification went: succeeded, partial (at least one file could not be classified), failed (none could) or skipped (no file needed the classifier). A file that fails classification comes back unclassified. The upload is never lost. A file the case already holds is not classified twice. Penbox matches on the attachment id, or on name + size + type. When both sides carry a checksum and the checksums differ, it is not a duplicate. The match runs against every file field of the case, not only the one the classifier would have picked: a document already filed elsewhere must not land under a second field. The row comes back skipped, with reason: duplicate and the key of the field that already holds the file. A document sent with an explicit key skips this check: it is filed as you asked.
A document row

Document Intelligence

Filing a document can trigger Document Intelligence. It runs only when the target file field has a Document Intelligence template configured on the case. It runs after the response, not during the request.
A document row with "intelligence": "pending" means values read from the file will land on the case shortly. Re-read GET /cases/{id} a moment later to see them. The change list of the input never carries them.
In suggest mode the file is uploaded but not filed. Document Intelligence therefore runs after the apply call, not after this one.
Document Intelligence is billed per page, exactly as it is on POST /document_intelligence. Classification is not billed separately.

Authorizations

Authorization
string
header
required

API token (starts with pnbx_). Create at https://app.penbox.io/workspace/settings/api. Include as: Authorization: Bearer {token}

Path Parameters

id
string<uuid>
required

Case UUID

Body

application/json
context

What this material is and where it comes from: free text, or any JSON (call id, agent, duration…)

Example:

"Transcript of an 11-minute call with the client on 2026-09-01"

content

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.

data
object

Explicit values, written as-is. An unknown key lands in the case's extra data, as with POST /cases/{id}/data.

contacts
object[]

Explicit contacts, same shape as POST /cases/{id}/contacts

documents
object[]

Files to file on the case, 5 at most. Each item is either an existing attachment id, or an inline base64 file. The whole body must stay under 50 MB.

Maximum array length: 5
options
object

Response

The input resource with its change list, plus the refreshed case

id
string<uuid>
created_at
string<date-time>
case
object

The case. On the write routes and on the single read it is the full case (same shape as GET /cases/{id}).

source
string

Provenance, inferred from your token: api

Example:

"api"

type
enum<string> | null

What the material is, derived by Penbox from the context and the content

Available options:
call,
note,
chat,
message,
document,
other
context
any | null

What you sent as context

mode
enum<string>
Available options:
suggest,
auto
status
enum<string>
Available options:
proposed,
partially_applied,
applied
intelligence
object
summary
string | null

One paragraph on what the material is about, in the case language

changes
object[]
unmapped
object[]

Facts read in the content that no case field can hold. Never written.

reviewed_at
string<date-time> | null
content
any | null

The raw material, verbatim. Only returned by GET /cases/{id}/inputs/{input_id}.