Skip to content

UploadDocumentWithHandler2 API

Finalizes a chunked file upload and creates a new document or a new version of an existing document at the specified path, with an optional version comment and explicit manual version numbers (Major.Minor.Revision). This extends UploadDocumentWithHandler1 by supporting the three-part manual version label that appears in the document's version list.

Endpoint

/srv.asmx/UploadDocumentWithHandler2

Methods

  • GET /srv.asmx/UploadDocumentWithHandler2?authenticationTicket=...&path=...&uploadHandler=...&versionComments=...&mpVersionMajor=...&mpVersionMinor=...&mpVersionRevision=...
  • POST /srv.asmx/UploadDocumentWithHandler2 (form data)
  • SOAP Action: http://tempuri.org/UploadDocumentWithHandler2

Parameters

Parameter Type Required Description
authenticationTicket string Yes Authentication ticket obtained from AuthenticateUser.
path string Yes Full infoRouter destination path including file name (e.g. /Finance/Reports/Q1-Report.pdf).
uploadHandler string (GUID) Yes The upload handler GUID returned by CreateUploadHandler after all chunks have been uploaded.
versionComments string No A comment describing the changes in this version.
mpVersionMajor short Yes Major component of the manual version label (e.g. 2 for version 2.0.1). Range: 1-"2400.
mpVersionMinor short Yes Minor component of the manual version label (e.g. 0 for version 2.0.1). Range: 0-"999.
mpVersionRevision short Yes Revision component of the manual version label (e.g. 1 for version 2.0.1). Range: 0-"999.

Chunked Upload Workflow

1. CreateUploadHandler        -' returns UploadHandler GUID + ChunkSize
2. UploadFileChunk            -' repeat until LastChunk=true
3. UploadDocumentWithHandler2 -' finalize with version comment and manual version number

Response

Success Response

<root success="true" DocumentId="12345" VersionId="1000003" />

Error Response

<root success="false" error="Invalid upload handler." />

Required Permissions

The calling user must have write (upload) permission on the destination folder.


Example

POST Request

POST /srv.asmx/UploadDocumentWithHandler2 HTTP/1.1
Content-Type: application/x-www-form-urlencoded

authenticationTicket=3f2504e0-4f89-11d3-9a0c-0305e82c3301
&path=/Finance/Reports/Q1-2024-Report.pdf
&uploadHandler=a1b2c3d4-e5f6-7890-abcd-ef1234567890
&versionComments=Released for review
&mpVersionMajor=2
&mpVersionMinor=0
&mpVersionRevision=1

SOAP Request

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
               xmlns:tns="http://tempuri.org/">
  <soap:Body>
    <tns:UploadDocumentWithHandler2>
      <tns:authenticationTicket>3f2504e0-4f89-11d3-9a0c-0305e82c3301</tns:authenticationTicket>
      <tns:path>/Finance/Reports/Q1-2024-Report.pdf</tns:path>
      <tns:uploadHandler>a1b2c3d4-e5f6-7890-abcd-ef1234567890</tns:uploadHandler>
      <tns:versionComments>Released for review</tns:versionComments>
      <tns:mpVersionMajor>2</tns:mpVersionMajor>
      <tns:mpVersionMinor>0</tns:mpVersionMinor>
      <tns:mpVersionRevision>1</tns:mpVersionRevision>
    </tns:UploadDocumentWithHandler2>
  </soap:Body>
</soap:Envelope>

JavaScript

Every call answers XML with HTTP 200, success or not, so success is the thing to branch on and errorCode is the number to report.

async function call(action, params) {
  const response = await fetch(`/srv.asmx/${action}?${new URLSearchParams(params)}`);
  const root = new DOMParser()
    .parseFromString(await response.text(), 'text/xml')
    .documentElement;

  if (root.getAttribute('success') !== 'true') {
    throw new Error(`${root.getAttribute('errorCode')}: ${root.getAttribute('error')}`);
  }
  return root;
}

Commits staged content and sets the version number explicitly, from a major, a minor and a revision.

await call('UploadDocumentWithHandler2', {
  authenticationTicket: ticket,
  Path: '/Finance/Reports/Q1.pdf',
  UploadHandler: handler,
  VersionComments: 'Release 2.1.3',
  MPVersionMajor: 2,
  MPVersionMinor: 1,
  MPVersionRevision: 3
});

Those three are packed into the single integer infoRouter stores: major * 1000000 + minor * 1000 + revision, so 2, 1, 3 becomes 2001003. This is the clearest demonstration in the API that a version number is not an ordinal - the second version of a document is 1000001, not 2000000.

Staging content

// 1. open a handler and read back the chunk size the server chose
const opened = await call('CreateUploadHandler', {
  authenticationTicket: ticket, PreferedChunkSize: 1048576
});
const handler = opened.getAttribute('UploadHandler');
const chunkSize = Number(opened.getAttribute('ChunkSize'));

// 2. send the file a chunk at a time, each with its own CRC32
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
  const chunk = bytes.slice(offset, offset + chunkSize);
  const last = offset + chunkSize >= bytes.length;

  let sent = await post('UploadFileChunk', {
    UploadHandler: handler,
    FileChunk: base64(chunk),
    ChunkHEXCRC: crc32Hex(chunk),        // uppercase hex, no padding
    LastChunk: String(last)
  });

  // a checksum mismatch asks for that chunk again rather than for the whole upload
  while (sent.getAttribute('tryagain') === 'true') {
    sent = await post('UploadFileChunk', { /* the same chunk */ });
  }
}

// 3. commit it - this consumes the handler
await call('UploadDocumentWithHandler', {
  authenticationTicket: ticket, Path: '/Finance/Reports/Q1.pdf', UploadHandler: handler
});

A handler is consumed by the call that commits it. Committing the same handler twice is 4000 "upload handler cannot be found". Abandon one that is no longer wanted with DeleteUploadHandler.

The upload family

Two ways in. Either the bytes travel with the call, or they are staged first and the call names the handler that holds them.

Bytes with the call Staged, by handler Adds
UploadDocument UploadDocumentWithHandler nothing
UploadDocument1 UploadDocumentWithHandler1 a version comment
UploadDocument2 the Checkout flag
UploadDocument3 UploadDocumentWithHandler2 a comment, and a checkout flag or a version number
UploadDocument4 UploadDocumentWithHandler3 a parameter document
UploadNewDocumentWidthHandler a folder and a name instead of one path

Uploading over a document that already exists is a new version, and only whoever holds the checkout may make one. Without a checkout the answer is 4000 "this document is not checked out". The Checkout flag means check it out for me if it is not already: the operation then checks out, uploads and checks back in, leaving the document free. It does not mean "leave it checked out".

Notes

  • The manual version label (e.g. 2.0.1) is a human-readable label separate from the internal infoRouter version ID.
  • Valid ranges: mpVersionMajor 1-"2400, mpVersionMinor 0-"999, mpVersionRevision 0-"999. Out-of-range values return an error.
  • Use UploadDocumentWithHandler3 for the most flexible option set (XML parameters string).


Error Codes

The errorCode values this operation returns, checked against a running server:

errorCode When
4010 the ticket is expired or unknown, or there is no ticket at all
4000 the upload handler is unknown, expired, or has already been committed
4000 a document is already at that path and is not checked out by the caller
4041 no folder at the parent of the path
4030 the caller may not add documents there, or the folder rules forbid the file type
HTTP 400 a required parameter was empty; refused by model binding, so there is no error document
4080 (REST) the request was still arriving when the FileUploadTimeOut time ran out; see GetGeneralAppSettings
Error Description
[900] Authentication failed Invalid or missing authentication ticket.
[901] Session expired or Invalid ticket The ticket has expired or does not exist.
Invalid upload handler. The GUID is not a valid or active upload handler.
Invalid version number component mpVersionMajor, mpVersionMinor, or mpVersionRevision is out of the valid range.
Folder not found The destination folder in the path does not exist.
Access denied The user does not have upload permission on the destination folder.
SystemError:... An unexpected server-side error occurred.