NextSign API
Documents

Create a Case from a Document

Fill a library document and send it for signing with POST /v3/api/documents/{id}/cases — the document-first way to create a case.

Creates a case whose document is one from your library (Dashboard → Documents), fills its fields with the values you send, and — unless you ask for a draft — sends it.

POST https://api.nextsign.dk/v3/api/documents/{id}/cases

Authenticated with an API key — see Authorization.

This is Create a Case with the document fixed by the URL. It takes the same recipients, settings and sender, runs through the same pipeline, and returns the same case object — the only thing that changes is that the body describes one document's values instead of a documents array. Use it when your integration starts from a template; use POST /v3/api/cases when it starts from a file, or needs several documents.

Quickstart

Find the document with List Documents, read its fields with Retrieve a Document, then:

curl https://api.nextsign.dk/v3/api/documents/6a394ed08ee8c3669f2c3e17/cases \
  -H "Authorization: Bearer $NEXTSIGN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Offer letter — Andreas Lauridsen",
    "recipients": [{ "name": "Andreas Lauridsen", "email": "al@example.com" }],
    "tags": {
      "name": "andreas",
      "role": "Engineer",
      "start_date": "2026-09-01",
      "is_senior": true,
      "terms": [{ "title": "Pay", "description": "Monthly, in arrears" }]
    }
  }'
const response = await fetch(
  `https://api.nextsign.dk/v3/api/documents/${documentId}/cases`,
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.NEXTSIGN_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      title: 'Offer letter — Andreas Lauridsen',
      recipients: [{ name: 'Andreas Lauridsen', email: 'al@example.com' }],
      tags: {
        name: 'andreas',
        role: 'Engineer',
        start_date: '2026-09-01',
        is_senior: true,
        terms: [{ title: 'Pay', description: 'Monthly, in arrears' }],
      },
    }),
  },
);

const { case: created } = await response.json();
console.log(created.recipients[0].signingUrl);
import os, requests

response = requests.post(
    f"https://api.nextsign.dk/v3/api/documents/{document_id}/cases",
    headers={"Authorization": f"Bearer {os.environ['NEXTSIGN_API_KEY']}"},
    json={
        "title": "Offer letter — Andreas Lauridsen",
        "recipients": [{"name": "Andreas Lauridsen", "email": "al@example.com"}],
        "tags": {
            "name": "andreas",
            "role": "Engineer",
            "start_date": "2026-09-01",
            "is_senior": True,
            "terms": [{"title": "Pay", "description": "Monthly, in arrears"}],
        },
    },
)

created = response.json()["case"]
print(created["recipients"][0]["signingUrl"])

You get back 201 with the whole case — the same object, field for field, that Create a Case returns, and a Location header pointing at /v3/api/cases/{id}:

201 Created
{
  "case": {
    "id": "6a87fdd9c2dce19673cd81d2",
    "referenceId": null,
    "title": "Offer letter — Andreas Lauridsen",
    "state": "open",
    "language": "da",
    "createdAt": "2026-08-21T10:12:41.803Z",
    "expiresAt": "2026-09-20T10:12:41.803Z",
    "folder": { "id": "65ab12cd34ef56ab78cd90a2", "name": "Default" },
    "sender": { "id": "65ab12cd34ef56ab78cd90ef", "name": "Anna Beck", "email": "anna@example.com" },
    "activeGroup": 0,
    "recipients": [
      {
        "id": "6a87fdd9c2dce19673cd81d3",
        "uid": "4t5ZQbtB",
        "name": "Andreas Lauridsen",
        "email": "al@example.com",
        "phone": "",
        "position": "",
        "group": 0,
        "type": "email",
        "signing": true,
        "status": "pending",
        "needsCpr": false,
        "signingSchema": "",
        "signingUrl": "https://www.nextsign.dk/sign/6a87fdd9c2dce19673cd81d2/2/h6gKDYSiPyYTC5Bkq6urKfKKW"
      }
    ],
    "documents": [
      {
        "id": "6a87fdd9c2dce19673cd81d4",
        "name": "Offer letter.pdf",
        "type": "application/pdf",
        "url": "https://nextsign-de.fsn1.your-objectstorage.com/65ab/production/Offer letter.pdf",
        "signObligated": true,
        "documentMustBeRead": false,
        "signatories": []
      }
    ],
    "delivery": {
      "attempted": true,
      "email": { "ok": true },
      "eboks": { "ok": true },
      "sms": { "ok": true },
      "webhook": { "ok": true }
    },
    "notices": [
      {
        "code": "sender-defaulted-to-key-creator",
        "message": "Neither senderId nor senderEmail was provided. The case was attributed to the API key's creator (anna@example.com)."
      },
      {
        "code": "default-signing-schemas",
        "message": "No settings.signingSchemas provided — the standard eIDs were used.",
        "using": [
          "urn:grn:authn:dk:mitid:low",
          "urn:grn:authn:dk:mitid:substantial",
          "urn:grn:authn:dk:mitid:business",
          "urn:grn:authn:se:bankid",
          "urn:grn:authn:de:personalausweis"
        ]
      }
    ]
  }
}

The .docx was filled with your values and rendered to PDF on the way in, so the attached document is Offer letter.pdf with type: application/pdf — both describe the file at url, not the library source.

Unknown fields are rejected — in the body and inside tags. A misspelled tag key is a 400, not a blank in the produced document. See Tags.

Path Parameter

ParameterTypeDescription
idstringThe document id, from List Documents

Query Parameters

ParameterTypeDefaultDescription
publicUrlsbooleanfalseReturn downloadable document links. See Document URLs

Request Body

Everything from Create a Case except documents and the case-level tags array, plus the document's own fields:

FieldTypeRequiredDefaultDescription
titlestringYesCase title. Max 300 characters
recipientsarrayConditionalMax 100. Required when state is open. See Recipients
tagsobjectNoValues for the document's fields, keyed by path. See Tags
versionIdstringNocurrent versionPin a specific version of the document. Must be one of its versions
signObligatedbooleanNotrueWhether signing the document is required. false attaches it for reading only
documentMustBeReadbooleanNofalseRequire each recipient to open it before signing
signatoriesarrayNoallIndexes into recipients — who signs the document. [0, 2] is the first and third recipient
statestringNoopenopen sends the case, draft saves it for later editing
referenceIdstringNoYour own reference. Max 200 characters
folderstringNocompany defaultFolder name or id. See Folders
settingsobjectNoLanguage, expiry, reminders, signing methods, … See Settings
messageTemplateIdstringNoTemplate for the message recipients receive
presetIdstringNoPreset id. See Presets
senderIdstringNothe key's creatorAttribute the case to a colleague, by user id. See Sender
senderEmailstringNothe key's creatorAttribute the case to a colleague, by email

There is no name: the attached file keeps the library version's own name, with .docx becoming .pdf once rendered.


Tags

tags is an object keyed by the path of each field in version.fields. Send nested fields as nested objects, lists as arrays of rows, and a template field as a string or { "text", "values" }:

{
  "tags": {
    "employee_name": "Andreas Lauridsen",
    "start_date": "2026-09-01",
    "weekly_hours": 37,
    "address": { "city": "Aarhus", "zip": "8000" },
    "other_benefits": [
      { "name": "Phone", "value": "Paid by employer" },
      { "name": "Pension", "value": "8% employer contribution" }
    ],
    "intro": { "text": "Dear {name}, welcome aboard.", "values": { "name": "Andreas" } }
  }
}

The values are checked against the document's field definitions before anything is created — the same checks the dashboard's "Document fields" step runs — and every problem comes back at once:

  • Unknown keys are rejected. So are values for an automatic field — a global, hidden or computed one — because the produced document would ignore or overwrite them without telling you.
  • Required fields must be filled, unless they carry a default. A required boolean must be true.
  • Types are enforced. A number field takes a JSON number, a boolean a boolean, an array a list of rows; a dropdown or radio takes one of its options[].value; a date must parse as a date; the author's minLength, maxLength, match, min and max bounds apply.
  • Conditional fields follow their rule. A field whose visible rule does not hold against the values you sent is neither required nor accepted. See Conditional fields.
400 Bad Request
{
  "error": "validation-failed",
  "details": [
    { "field": "tags.nope", "message": "unknown field" },
    { "field": "tags.company_name", "message": "is a global field and is filled from the account" },
    { "field": "tags.name", "message": "must be one of: inam, andreas" },
    { "field": "tags.is_senior", "message": "must be a boolean" },
    { "field": "tags.terms[0].bogus", "message": "unknown field" },
    { "field": "tags.employee_name", "message": "is required" }
  ]
}

Values are inserted as you send them. Automatic fields — the company name, today's date, the sender's details — are added at production time, and hidden and computed fields resolve from their definitions; you never need to send those.

A pdf document has no fields. Sending tags with any key for one returns 400 at tags (an empty object is accepted). Check format on the document first.

Drafts keep the values editable

With state: "draft" the document is attached by reference and not rendered: the draft opens in the dashboard's case builder with your tags pre-filled and still editable, and the PDF is produced when someone sends it from there. In the response the document has the library file's name and type, and url is null until then.


Errors

errorStatusMeaning
validation-failed400A body field or a tag value is invalid, or versionId is not a version of this document. See details
document-not-found404No such document in your library. See What you see
document-production-failed400The document could not be rendered with these values — usually a .docx that Word itself would refuse to open
invalid-folder400No folder with that name or id
message-template-not-found400messageTemplateId is not found for your company
preset-not-found400Not found for your company
sender-user-not-found400senderId or senderEmail is not a member of your company
invalid-signing-page-logo400settings.logo is not a PNG/JPEG, and not a NextSign URL

Field paths are relative to the body — tags.start_date, signatories[0], versionId — rather than the documents[0].… form the generic endpoint uses.

Every 4xx from this endpoint means nothing was created. The document is rendered and the case persisted only after all of these have passed, so a 4xx is always safe to fix and resend. Full detail in Errors.