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}/casesAuthenticated 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}:
{
"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
| Parameter | Type | Description |
|---|---|---|
id | string | The document id, from List Documents |
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
publicUrls | boolean | false | Return 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:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
title | string | Yes | — | Case title. Max 300 characters |
recipients | array | Conditional | — | Max 100. Required when state is open. See Recipients |
tags | object | No | — | Values for the document's fields, keyed by path. See Tags |
versionId | string | No | current version | Pin a specific version of the document. Must be one of its versions |
signObligated | boolean | No | true | Whether signing the document is required. false attaches it for reading only |
documentMustBeRead | boolean | No | false | Require each recipient to open it before signing |
signatories | array | No | all | Indexes into recipients — who signs the document. [0, 2] is the first and third recipient |
state | string | No | open | open sends the case, draft saves it for later editing |
referenceId | string | No | — | Your own reference. Max 200 characters |
folder | string | No | company default | Folder name or id. See Folders |
settings | object | No | — | Language, expiry, reminders, signing methods, … See Settings |
messageTemplateId | string | No | — | Template for the message recipients receive |
presetId | string | No | — | Preset id. See Presets |
senderId | string | No | the key's creator | Attribute the case to a colleague, by user id. See Sender |
senderEmail | string | No | the key's creator | Attribute 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 requiredbooleanmust betrue. - Types are enforced. A
numberfield takes a JSON number, abooleana boolean, anarraya list of rows; adropdownorradiotakes one of itsoptions[].value; adatemust parse as a date; the author'sminLength,maxLength,match,minandmaxbounds apply. - Conditional fields follow their rule. A field whose
visiblerule does not hold against the values you sent is neither required nor accepted. See Conditional fields.
{
"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
error | Status | Meaning |
|---|---|---|
validation-failed | 400 | A body field or a tag value is invalid, or versionId is not a version of this document. See details |
document-not-found | 404 | No such document in your library. See What you see |
document-production-failed | 400 | The document could not be rendered with these values — usually a .docx that Word itself would refuse to open |
invalid-folder | 400 | No folder with that name or id |
message-template-not-found | 400 | messageTemplateId is not found for your company |
preset-not-found | 400 | Not found for your company |
sender-user-not-found | 400 | senderId or senderEmail is not a member of your company |
invalid-signing-page-logo | 400 | settings.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.