Create a new case file with documents and signers

Creates a new case file in Penneo with the specified documents and signers. This endpoint accepts a multipart/form-data request containing JSON metadata and files.

Recent Requests
Log in to see full request history
TimeStatusUser Agent
Retrieving recent requests…
LoadingLoading…

Open API 3.0 Specs

The Open API 3.0 Specs are available on the following urls:

Case File Data Structure Documentation

This document describes the complete JSON structure for the data field when creating a Penneo case file.

Table of Contents


Overview

The data field must be a stringified JSON object containing a caseFile property. This structure defines all aspects of your case file including title, signers, documents, and various configuration options.

Format: The data must be sent as a JSON string, not as a JSON object.


Root Structure

{
  "caseFile": {
    // Case file configuration (see below)
  }
}

Properties

PropertyTypeRequiredDescription
caseFileobjectYesThe main case file configuration object

Case File Object

The caseFile object contains all configuration for the case file.

Required Properties

PropertyTypeDescriptionConstraints
titlestringThe case file title displayed in PenneoMin: 1 char, Max: 1024 chars
signersarrayArray of signer objectsAt least 1 signer required
documentsarrayArray of document objectsAt least 1 signable document required

Optional CaseFile properties

PropertyTypeDefaultDescription
activateCaseFilebooleantrueWhen true, the case file is activated/sent and signing links are returned. When false, the case file is left unsent and signingLinks is [].
ccRecipientsarray-Recipients who receive a copy but don't sign
languagestring | nullnullCase file language. Applied to all signers without a specific language. Options: 'en', 'da', 'sv', 'nl', 'fr', 'fi', 'no', 'de', or null
metaDatastring-Metadata searchable in Penneo
referencestring-Reference shown in Penneo web application
folderIdinteger-Folder ID where case file should be placed (min: 1)
sendAtinteger | nullnullUnix timestamp when case file should be sent. null = immediately
expireAtinteger | nullnullUnix timestamp when case file will expire
sensitiveDatabooleanfalseIf true, signers must validate their identity before accessing documents
visibilityModeinteger0Document visibility: 0 = all docs visible, 1 = only docs signer must sign
disableNotificationsOwnerbooleanfalseIf true, case file owner won't receive notifications
disableEmailAttachmentsbooleanfalseDisables email attachments in finalization emails (overridden if sensitiveData is true)
senderNamestring | nullnullCustom sender display name for outgoing emails (2–255 chars). Leave empty or null to use the default.
signOnMeetingbooleanfalseEnable signing documents on a meeting

Signers Array

Each signer represents a person who will sign or review the documents.

Required Properties

PropertyTypeDescriptionConstraints
namestringName of the signerMin: 1 character

Optional Properties

PropertyTypeDescriptionConstraints
emailstringSigner's email address. Used for email delivery. Unused when channel is eboksValid email format
channelstringDelivery channel. Defaults to email when omitted. eboks delivers via e-Boks'email' or 'eboks'
cprstringExactly 10-digit CPR number for e-Boks. Mutually exclusive with cvr. Only valid when channel is eboksExactly 10 digits. Required (without cvr) when channel is eboks
cvrstring8-digit CVR for e-Boks, optionally with an alphanumeric :suffix. Mutually exclusive with cpr. Only valid when channel is eboks8 digits, optional alphanumeric :suffix. Required (without cpr) when channel is eboks
socialSecurityNumberPlainstring | nullSigner's SSN or phone numberE.164 format recommended for phone
vatinstring | nullVAT identification number of signer's company-
rolestringRole identifier for the signer. If no role is provided, "Signer" will be used as the default role for all signersMin: 3 chars. Any characters allowed. Must not start or end with whitespace.
onBehalfOfstringCompany/organization that this signer signs on behalf of-
signOrderintegerSigning order (0 = can sign immediately)Min: 0
ssnTypestringType of SSN/mobile identification'legacy', 'dk:cpr', 'se:pin', 'no:nin', 'be:nrn', 'fi:pic', 'sms'
accessControlbooleanIf enabled, SSN/VATIN/phone is validated before access-
successUrlstringRedirect URL after successful signingValid URI, min: 5 chars
failUrlstringRedirect URL after signing failureValid URI, min: 5 chars
languagestring | nullSigner's preferred language'en', 'da', 'sv', 'nl', 'fr', 'fi', 'no', 'de', or null
emailFormatstringEmail format'text' or 'html'
emailSubjectstringCustom email subjectMin: 5 chars
emailTextstringCustom email body textMin: 5 chars
reminderIntervalintegerDays before reminder is sentMin: 1
reminderEmailSubjectstringCustom reminder email subjectMin: 5 chars
reminderEmailTextstringCustom reminder email bodyMin: 5 chars
completedEmailSubjectstringCustom completion email subjectMin: 5 chars
completedEmailTextstringCustom completion email bodyMin: 5 chars
activeAtinteger | nullUnix timestamp when signer becomes active
expireAtinteger | nullUnix timestamp when signer access expires
enableInsecureSigningbooleanEnable insecure signing methods
insecureSigningMethodsarrayIf insecure signing methods are enabled, choose which ones are available for the signerArray of allowed insecure signing methods: 'text', 'draw', 'image'
secureSigningMethodsarrayThe allowed secure electronic signing methods per signer. Choose only the ones that are enabled for the customerArray of: 'mitid.dk', 'bankid_se', 'bankid_no', 'itsme.be', 'ftn.fi', 'sms', 'eid.belgium.be', 'passport-reader.eident.dk', 'ausweisapp.bund.de', 'qes.personal.mitid.dk', 'qes.erhverv.mitid.dk'
storeAsContactboolean-If true, signer will be stored as contact in Penneo

Documents Array

Each document represents a file to be signed or attached to the case file.

Required Properties

PropertyTypeDescriptionConstraints
titlestringDocument titleMin: 1 char, Max: 1024 chars
namestringFile name (must match uploaded file)Min: 1 character

Optional Properties

PropertyTypeDefaultDescriptionConstraints
signablebooleantrueIf true, creates signable document. If false, document is a non-signable attachment-
rolesarray-Array of role identifiers that can sign this documentEach role: min 3 chars. Any characters allowed. Must not start or end with whitespace.
documentOrderinteger-Display order of the documentMin: 0
metaDatastring-Metadata searchable in Penneo-

CC Recipients Array [Optional]

CC Recipients are people who should receive a copy of the case file documents but do not need to sign. They will be notified when the case file is completed.

Required Properties

PropertyTypeDescriptionConstraints
namestringName of the recipient.Min: 1 character
emailstringEmail of the recipient.Valid email format

Optional Properties

PropertyTypeDescriptionConstraints
storeAsContactbooleanSigner will be stored as a contact. Defaults to true.-

Minimal Example

Here's a minimal example with only required fields:

{
  "caseFile": {
    "title": "Simple Agreement",
    "signers": [
      {
        "name": "John Doe",
        "email": "[email protected]"
      }
    ],
    "documents": [
      {
        "title": "Agreement Document",
        "name": "agreement.pdf"
      }
    ]
  }
}

Extended Example

Here's a comprehensive example with most fields populated:

Make sure you pass in the files array of the main payload the corresponding files with names matching the name properties in the documents array. In our example you will have to submit terms.pdf and contract.pdf files.

You can also pass a folderId in the caseFile object to place the case file in a specific folder.

Set activateCaseFile to false on the caseFile object if you want to create the case file without activating/sending it. In that case the job result still returns caseFile.id, but signingLinks is [].

{
  "caseFile": {
    "title": "Commercial Agreement 2025",
    "senderName": "Acme Signing Department",
    "language": "en",
    "reference": "CONTRACT-2025-001",
    "metaData": "Q4 Commercial Contract",
    "sensitiveData": false,
    "disableNotificationsOwner": false,
    "disableEmailAttachments": false,
    "signOnMeeting": false,
    "sendAt": null,
    "expireAt": null,
    "visibilityMode": 0,
    "signers": [
      {
        "name": "John Doe",
        "email": "[email protected]",
        "role": "Client Signer",
        "language": "en",
        "socialSecurityNumberPlain": null,
        "ssnType": "dk:cpr",
        "vatin": null,
        "signOrder": 0,
        "onBehalfOf": "Acme Corporation",
        "accessControl": true,
        "storeAsContact": true,
        "successUrl": "https://example.com/success",
        "failUrl": "https://example.com/failure",
        "emailFormat": "html",
        "emailSubject": "Please sign the commercial agreement",
        "emailText": "Dear John, please review and sign the attached commercial agreement.",
        "reminderInterval": 3,
        "reminderEmailSubject": "Reminder: Please sign the commercial agreement",
        "reminderEmailText": "This is a friendly reminder to sign the agreement.",
        "completedEmailSubject": "Thank you for signing",
        "completedEmailText": "The agreement has been completed successfully.",
        "activeAt": null,
        "expireAt": null,
        "enableInsecureSigning": false,
        "insecureSigningMethods": [],
        "secureSigningMethods": [
          "mitid.dk",
          "bankid_se",
          "bankid_no",
          "itsme.be",
          "ftn.fi",
          "eid.belgium.be",
          "passport-reader.eident.dk",
          "ausweisapp.bund.de",
          "qes.personal.mitid.dk",
          "qes.erhverv.mitid.dk"
        ]
      },
      {
        "name": "Jane Smith",
        "email": "[email protected]",
        "role": "Company Signer",
        "channel": "eboks",
        "cpr": "1234567890",
        "language": "en",
        "signOrder": 1,
        "onBehalfOf": "Company Ltd",
        "accessControl": false,
        "storeAsContact": true,
        "emailFormat": "text",
        "reminderInterval": 2
      }
    ],
    "ccRecipients": [
      {
        "name": "John CC",
        "email": "[email protected]",
        "storeAsContact": false
      },
      {
        "name": "Jane CC",
        "email": "[email protected]",
        "storeAsContact": true
      }
    ],
    "documents": [
      {
        "title": "Main Contract",
        "name": "contract.pdf",
        "signable": true,
        "roles": [
          "Client Signer",
          "Company Signer"
        ],
        "documentOrder": 0,
        "metaData": "Primary contract document"
      },
      {
        "title": "Terms and Conditions",
        "name": "terms.pdf",
        "signable": false,
        "metaData": "Standard T&C attachment"
      }
    ]
  }
}

Note:
secureSigningMethods field contains all the available secure signing methods for explanatory purposes. Your customer most likely has only some of these methods enabled. In order to have a successful send, use only the methods that are available for your account. If you want to use all available methods, do not pass the secureSigningMethods field at all and we will automatically use all methods enabled for your account. If you want to use only a subset of the available methods, pass the secureSigningMethods field with only the methods you want to use. If you want to use no secure signing methods (SES-only when insecure signing is enabled), pass an empty array in the secureSigningMethods field.


Usage Notes

1. Sending the Data

The data field must be stringified when sending in a multipart/form-data request:

import { readFileSync } from "fs";

// 1. Build CaseFile JSON data
const caseFileData = {caseFile: {
    title: "My Contract",
    signers: [
      { name: "John Doe", email: "[email protected]" },
      { name: "Jane Doe", email: "[email protected]" }
    ],
    documents: [
      { title: "Contract", name: "contract.pdf" }, //name matches first uploaded doc
      { title: "Appendix", name: "appendix.pdf" }  //name matches second uploaded doc
    ]
  }};

// 2. Load files directly into File objects
const contractPdfBytes = readFileSync("./contract.pdf");
const appendixPdfBytes = readFileSync("./appendix.pdf");

const contractFile = new File([contractPdfBytes], "contract.pdf", {
  type: "application/pdf"
});

const appendixFile = new File([appendixPdfBytes], "appendix.pdf", {
  type: "application/pdf"
});

// 3. FormData
const formData = new FormData();

// JSON data field
formData.append(
  "data",
  JSON.stringify(caseFileData) // <-- Append the JSON as a simple string
);

// Add PDF files
formData.append("files", contractFile, contractFile.name);
formData.append("files", appendixFile, appendixFile.name);

// 4. POST request
const response = await fetch(
  "https://sandbox.penneo.com/send/api/v1/casefiles/20251022/create",
  {
    method: "POST",
    headers: {
      "X-Auth-Token": "YOUR_TOKEN_HERE" // Retrive the JWT Token based on https://penneo.readme.io/v1.3/docs/using-oauth#/api-keys-grant
    },
    body: formData
  }
);

// 5. Handle Penneo response
if (!response.ok) {
  console.error("Status code:", await response.status);
  console.error("Error:", await response.text());
  process.exit(1);
}

console.log("Success:", await response.json());

2. Role Matching

  • If you specify roles in documents, only signers with matching role values can sign those documents
  • Roles must be at least 3 characters. Any characters are allowed, including letters, numbers, spaces, and punctuation. Roles must not start or end with whitespace
  • If no roles are specified on documents, all signers can sign all documents

3. File Matching

  • The name property in each document must match the filename of the uploaded PDF

4. Communication channel (e-Boks)

  • Omit channel, or set it to 'email', to deliver via email (the default)
  • Set channel to 'eboks' to deliver via e-Boks. Provide exactly one of cpr (exactly 10 digits) or cvr (8 digits, optional alphanumeric :suffix)
  • Do not send cpr or cvr unless channel is 'eboks'
  • The recipient name is always the signer name. Language is the signer language, then the case file language, then 'da' when neither is set.
  • Email is not necessary for e-Boks delivery
  • The customer must have e-Boks enabled. Any customer admin can request this feature from ConfigurationCompanyCommunication Channels section.

5. Timestamps

All timestamp fields (sendAt, expireAt, activeAt) use Unix epoch time (seconds since January 1, 1970 UTC).

// JavaScript example to get Unix timestamp
const timestamp = Math.floor(Date.now() / 1000);

Error Handling

If validation fails, you'll receive a 400 Bad Request response with details about which fields failed validation. Common errors:

  • Missing required fields: Ensure title, signers, and documents are provided
  • Invalid role format: Roles cannot start or end with whitespace, and must be at least 3 characters
  • Email format: Must be a valid email address
  • String length: Check minimum/maximum length requirements
  • Invalid enum values: Language, ssnType, emailFormat, and channel must use exact values listed above
  • e-Boks identifier: When channel is eboks, provide exactly one of cpr or cvr
  • e-Boks not enabled: The customer must have e-Boks enabled. Failed jobs report: e-Boks is an add-on service that is currently disabled for this customer. To activate it, the customer needs to sign an e-Boks appendix and accept the extra pricing. Please reach out to the Penneo Sales team to generate the contract and have e-Boks enabled.
  • File mismatch: Document name must match uploaded PDF filename

Credentials & Testing

To interact with the Penneo API, you need to authenticate using JWT tokens.

Follow these steps:

  • Make sure you have your OAuth Client ID and Client Secret for the environment you are implementing. If you don't have a set, contact Penneo support to have them issued.
  • Generate a JWT token as described in the Authentication Guide using API Keys Grant.
  • With the JWT Token, you can make authorized requests to this endpoint for the lifetime of the JWT Token.
  • Once you submit a CaseFile, you can check its status using the Check job status by UUID endpoint. We support polling and we encourage you to check for a job status no less than every 5 seconds until the job is complete.

Checking Job Status

When you submit a case file, the job is created asynchronously and the response returns a job UUID and payloadHash. Use these two values with the Check job status by UUID endpoint to poll for completion and obtain the case file ID.

The case file ID is what you use to track the case file afterwards (for example, its signing progress).

Job status records are retained for 14 days after the job is created, after which the status endpoint returns 404 Not Found. This retention applies only to the job status record — the case file itself is not deleted and remains available in Penneo.

Body Params

Multipart form data containing the case file configuration (as JSON string in 'data' field) and files to upload.

string
required

JSON string containing the case file configuration. Must be a stringified JSON object with a required 'caseFile' property (title, signers array, and documents array). Optional caseFile.activateCaseFile (boolean, default true): when false, the case file is created but not activated/sent, and signingLinks in the job result is an empty array. Each signer supports optional channel ('email' or 'eboks'; default email). e-Boks requires exactly one of cpr (exactly 10 digits) or cvr (8 digits with an optional alphanumeric :suffix); cpr and cvr are mutually exclusive and only valid when channel is eboks. e-Boks must be enabled for the customer; email is unused for e-Boks delivery. Each signer also supports optional secureSigningMethods (array of allowed secure electronic signing methods; allowed values: 'mitid.dk', 'bankid_se', 'bankid_no', 'itsme.be', 'ftn.fi', 'sms', 'eid.belgium.be', 'passport-reader.eident.dk', 'ausweisapp.bund.de', 'qes.personal.mitid.dk', 'qes.erhverv.mitid.dk') which overrides case file and customer settings when set. Pass an empty array to disable all secure methods (SES-only when insecure signing is enabled); omit the field to use case file / customer defaults.

files
array of files
required

Array of PDF files to upload. The number of files must match the number of documents specified in the data field. Files should be uploaded in the same order as documents are specified.

Headers
string
required

JWT token for authentication. Obtain a token following the instructions at https://penneo.readme.io/docs/using-oauth#authorization-code-grant. The token must be valid and have the necessary permissions to create case files.

Responses

Language
URL
LoadingLoading…
Response
Click Try It! to start a request and see the response here! Or choose an example:
application/json