Migrating from v2
Field-by-field mapping from POST /api/v2/{company}/case/create to POST /v3/api/cases, and every behavioural difference.
This page maps v2 Case Create onto v3 Create a Case and lists the behaviour that differs.
v2 is not deprecated and is not changing. You can migrate case creation to v3 and keep using v2 for listing, updating, forms, documents and webhooks — both surfaces read and write the same data.
Start here
Three changes will affect almost every existing payload:
- Unknown fields are rejected. Anything v2 quietly ignored is now a
400. Expect your first v3 request to fail, and read thedetailsarray — it names every field at once. recipients[].signingnow defaults totrue. In v2 it defaults tofalse. If you relied on that, set"signing": falseexplicitly on observers.- Cases expire in 30 days by default, not 90. Send
settings.expiresInDaysif you depended on v2's default.
Endpoint and envelope
| v2 | v3 | |
|---|---|---|
| URL | POST /api/v2/{company}/case/create | POST /v3/api/cases |
| Host | https://www.nextsign.dk | https://api.nextsign.dk |
| Company | path, body, or query | from the API key |
| Body wrapper | body or body.values both accepted | body only |
| Success | 200 { data, notices } | 201 { case } + Location header |
| Failure | { errors: [{ message }] }, sometimes with 200 | { error, details }, always 4xx |
Field mapping
Case
| v2 | v3 | Note |
|---|---|---|
title or name | title | The name alias is gone |
referenceId | referenceId | |
type | not accepted | Every v3 case is Simple sign |
autoSend | settings.autoSend | |
| — | state | New: draft creates without sending |
folder | folder | Now rejects a name that does not exist |
roles | removed | Always taken from the folder |
settings.lang | settings.language | |
settings.availability.days | settings.expiresInDays | Default changed: 90 → 30 |
settings.reminders.send | settings.reminders.send | |
settings.reminders.amount | settings.reminders.amount | Now bounded 1–20 |
settings.reminders.daysBetween | settings.reminders.daysBetween | Now bounded 1–365 |
reminderAutoSend | settings.reminders.autoSend | |
settings.deletion | not available | Company default applies |
settings.allowRecipientsViewOthers | not available | Company default applies |
template | settings.template | |
emailtemplate or emailTemplate | messageTemplateId | Now verified to belong to your company |
customMessage + message | settings.message | Sending settings.message is enough |
user | senderId | Now verified to be a company member |
user_email | senderEmail | Still looks a user up, but they must be a company member |
user_name | not accepted | The display name comes from the sender's profile |
signingSchemas | settings.signingSchemas | Unknown values now rejected |
tags | tags | |
signingPage.logo | settings.logo | |
integrations.microsoft.returnPath | settings.integrations.microsoft.returnPath | Must now begin with / |
realtime | not available | |
| — | presetId | New: applies your company's preset |
Recipients
| v2 | v3 | Note |
|---|---|---|
group or order | group | The order alias is gone |
signing | signing | Default changed: false → true |
template | messageTemplateId | |
type: "phone" | type: "sms" | |
| everything else | unchanged |
Documents
| v2 | v3 |
|---|---|
file + fileIsBlob: true | content |
file + fileIsBlob: false | url |
document_id | not accepted |
| — | documentId + versionId — attach from your document library |
name optional | name required |
Behaviour differences
Things that will change your results
Cases expire in 30 days by default, not 90. v2's 90 was an API-only outlier; every other part of NextSign uses 30.
recipients[].signing defaults to true. Omitting it in v2 creates a case nobody can sign, with no warning.
The signing page language follows your company, then falls back to Danish. v2 always used Danish for API-created cases regardless of company settings.
Sequential signing works. v2 never set the group cursor on API-created cases, so group ordering did not gate correctly. v3 sets it, and it is returned as activeGroup.
An unknown folder is an error. v2 silently files the case in the default folder — which has a different role list, so the case ends up visible to a different set of colleagues.
Unrecognised file content is rejected — when you send the bytes. v2 stored anything it could not identify as a PDF, so a JPEG became a "PDF" that broke the signing page later. v3 inspects content and rejects anything that is not a PDF or a real .docx. A document sent as a url is still stored by reference and not inspected, in v3 as in v2.
roles cannot be sent. In v2 a client-supplied roles array widens who inside your company can see the case, bypassing the folder's access list.
Sender attribution never guesses silently. With no user, v2 assigns the case to whichever company user sorts first. v3 uses the user you name in senderId or senderEmail; with neither, it falls back to the person who created the key and tells you so in notices. A key with no user behind it is rejected outright.
You cannot set a sender name or email freely. v2 takes user_name and user_email straight from the payload, so a case can claim to come from any address. In v3 those values are read from the named user's NextSign profile.
Things that make failures easier to handle
A 4xx means nothing was created. v2 can return 500 for a case that exists and has already been emailed, because sending happens after the save and is not isolated — retry that and you get a duplicate. v3 has no such window; see Errors.
Send failures are reported, not thrown. A failed email comes back as delivery.email.ok: false on a 201.
One contact failure no longer fails the case. v2 aborts the whole creation if a recipient cannot be written to your contact list.
All validation errors arrive together, each with a field path.
Response differences
| v2 | v3 | |
|---|---|---|
| Case id | data._id | case.id |
| Signing link | data.recipients[].url | case.recipients[].signingUrl |
| Recipient handle | data.recipients[].uid | case.recipients[].uid — and case.recipients[].id is its _id |
| Document link | data.documents[].file | case.documents[].url |
| Signing status | data.recipients[].signed | case.recipients[].status |
| Send outcome | not reported | case.delivery |
v3 returns a defined object rather than the raw database document, so signingToken, nextSignKey, encrypted cpr, signer, logs, events, roles and __v are no longer present.
If your integration reads signingToken to build signing links itself, switch to signingUrl. Hand-built links break when the signing domain, language prefix or template number changes — all three are resolved per case on our side.
Worked example
v2:
{
"title": "Lejeaftale",
"user_email": "info@example.com",
"settings": {
"lang": "da",
"availability": { "days": 14 },
"reminders": { "send": true, "amount": 2, "daysBetween": 3 }
},
"recipients": [
{ "name": "Andreas", "email": "al@example.com", "signing": true, "order": 0 }
],
"documents": [
{ "name": "Lejekontrakt.pdf", "file": "JVBERi0...", "fileIsBlob": true }
]
}v3:
{
"title": "Lejeaftale",
"senderEmail": "anna@example.com",
"settings": {
"language": "da",
"expiresInDays": 14,
"reminders": { "send": true, "amount": 2, "daysBetween": 3 }
},
"recipients": [
{ "name": "Andreas", "email": "al@example.com", "group": 0 }
],
"documents": [
{ "name": "Lejekontrakt.pdf", "content": "JVBERi0..." }
]
}user_email in v2 is a display value. senderEmail in v3 looks up a colleague, so it has to be the address of a NextSign user in your company — a generic info@ mailbox that nobody signs in with is rejected with sender-user-not-found.
Suggested rollout
Create a separate key
Mint a new API key for the v3 integration, so you can revoke it without touching anything else.
Send your existing payload
Post it to /v3/api/cases in the test environment and read the details array. Every wrong field is named at once.
Rename until it passes
Then compare the created case in the dashboard against one made through v2 — folder, sender, signing order, documents.
Move production traffic
Keep the v2 path available to fall back to until you are confident.