Skip to main content

 

Documentation & User Guides | FotoWare

Uploading assets using the API

Applications and integrations can ingest assets into FotoWeb by using the Upload API.

Assets can be added to archives either by copying or moving them from other archives or by uploading new files. The upload API also supports creating new folders and folder hierarchies and applying metadata to assets immediately upon ingestion.

Required permissions

Upload requires authentication.

  • If server-to-server authentication is used, then the client can upload assets at any location where upload is supported.
  • If the client is impersonating a specific user (e.g., when using user server-to-server API authentication), then that user must have upload permission on the upload location.

Request URL

Assets are uploaded by making a POST request to the URL of the collection to upload to. Each request can contain one or more assets.

It is also possible to create a new folder (or a hierarchy of nested folders) and upload assets to the new folder. This is done by adding all new folders to the end of the URL as follows:

POST upload_location_url/

where

Parameter

Description

upload_location_url URL of the collection (e.g., archive or folder) to upload to (upload location)

Note:

  • The upload location URL (upload_location_url) must be the URL found in the uploadHref attribute of the collection representation of the selected upload destination collection. Do NOT use the URLs from the data or href attributes. Please do not hard-code upload location URLs.
  • To get the upload URLs of suitable upload destinations, please see Ingestion. This API can also be used for creating an interface that allows the user to choose an upload destination.
  • To get the upload request URL of an already known collection (with a known API URL), first request the collection representation of that collection, then use the value of the uploadHref attribute for the upload request.

Which collections support uploading?

To determine the upload location (which is the request URL of the upload request), you must first find a collection that supports upload.

Assets can only be uploaded to archives. Furthermore, assets can only be uploaded to certain locations within archives:

  • If the archive has a custom input folder, then assets can only be uploaded to the root URL of the archive. It is not possible to upload to a specific sub folder or to create a new sub folder during upload.
  • If the archive does not have a custom input folder, then assets can only be uploaded to physical folders. A physical folder is any sub folder that represents an actual folder on disk. For example, the root of an archive or the root of a union member is NOT a physical folder.

It is recommended to use the upload destination list API to discover possible upload locations and/r to check the properties of a particular collection to see if it is a valid upload location. For more information, see ingestion.

Request body

Upload uses HTTP Multipart requests, as defined in RFC1341 (external link).

Note:

You usually do not need to implement RFC1341 by yourself. Most likely, the HTTP client library of your programming platform which you use for sending HTTP requests has built-in support for HTTP multipart requests. This is the standard format for uploading files, so check if your HTTP client library has functions for attaching files to requests.

Here is an example of a complete upload request for one file:

POST /fotoweb/archives/5000-Wirefeed/Incoming/
Accept: application/vnd.fotoware.upload-response+json
Content-Type: multipart/form-data; boundary=WUG7zDvyA3hVq16Q
authentication headers

--WUG7zDvyA3hVq16Q
Content-Disposition: form-data; name="folder"
 
folder1/folder2/.../
--WUG7zDvyA3hVq16Q
Content-Disposition: form-data; name="Filedata"; filename="image.jpg"
Content-Type: image/jpeg
 
binary image data
--WUG7zDvyA3hVq16Q--

Requests MUST include the following header:

Accept: application/vnd.fotoware.upload-response+json

This is for backward-compatibility reasons. Omitting this exact header will invoke the old (synchronous) behavior of the upload API, which is no longer recommended, due to issues with large files and ambiguity on folder creation. Existing integrations using the old upload API will continue to work, but synchronous uploads are no longer recommended and no longer documented here. We recommend updating old upload integrations.

 

where: 

Parameter

Description

boundary

Boundary between parts of the request. Can be chosen freely by the client. The string is the same for all parts of the request. It should be different for each request, random and long enough to minimize the chance that it occurs in the files being uploaded. HTTP client libraries usually take care of this. In the example above, "WUG7zDvyA3hVq16Q" was chosen as the boundary string. Note that the boundary string is preceded by two dashes (--) at the end of each part.
filename The filename that the asset will have in FotoWeb after uploading. Must be specified for each part of the request that contains a file. The filename can be chosen freely and does not need to be identical to the filename of the file on the local disk on the client. HTTP client library functions should expect this as a parameter.
Content-Type Ignored by FotoWeb. Should match the MIME type of the file, but can safely be set to application/octet-stream regardless of file type.
folder1/folder2/.../

Hierarchy of new folders to be created at the destination.

This parameter is optional. If it is not present, then all assets will be uploaded to the collection specified by the request URL.

If this parameter is present, then it must contain a list of nested folders, separated by slashes, to be created in the collection specified by the requested URL. All assets will be uploaded to the innermost folder.

  • Using this parameter is the only way of creating a new folder during upload. The folder specified by the request URL must already exist, or the request will fail.
  • New folders can only be created if the canCreateFolders flag is true for the collection assets are being uploaded to. For more information, see Ingestion.
  • Folder names are expected to be encoded in UTF-8 and must be specified "plain text" without any escaping (such as "percent-encoding" or "HTML-encoding") of any characters.
  • Each folder name must be a valid Windows folder name. Folder names are case-insensitive. The server may make adjustments to the name, such as normalization of accented characters.
     

Note:
All blank lines are required, and all line breaks that are part of the request body syntax must be in CRLF (carriage-return + line-feed; "\r\n") format.

 

Update Response and Status Polling

Once the file transfer has finished, the API returns a response with status code 202 Accepted. At this time, the upload is not finished. The server has to do further processing, such as applying metadata, doing consistency and security checks, copying the file to its storage location, and generating renditions. It is not guaranteed that all of these operations will complete successfully. Clients should not attempt to make any API requests that rely on the existence of the uploaded assets before the upload has finally finished successfully.

 

When an upload has finished, and assuming no further changes have been made, the following guarantees are made:

  • An API request to the asset returns a valid result.
  • The asset is contained in the asset list of the collection containing it, as long as the asset list is requested with the default sorting order and no search query on the collection.
  • The asset is contained in the asset list of the collection containing it, if the asset list is requested with a search query matching the asset.
  • Quick renditions, previews and thumbnails of the asset are available via the API.

The following are NOT guaranteed:

  • Video renditions are NOT NECESSARILY available. A client can check the status of video renditions of an asset by requesting its API representation
  • The asset is NOT NECESSARILY returned in search results that should match it. Search indexing may be delayed
  • The asset is NOT NECESSARILY contained in the asset list of the collection containing it, if the asset list is requested with a non-default sorting order.

An upload webhook, if configured, fires once an upload has finished successfully.

 

The Location header of the response is a URL which can be used for polling the status of the upload.

202 Accepted
Content-Type: application/vnd.fotoware.upload-response+json
Location: http://FOTOWEB_HOSTNAME/fotoweb/me/background-tasks/0735ca40-3a93-11e9-ae8d-34e12de8a773

with a request body:

{
  "href": "/fotoweb/me/background-tasks/0735ca40-3a93-11e9-ae8d-34e12de8a773"
}

where

Attribute Type Description
href URL (string) Relative URL of the background task

The Location header as well as the href attribute are the URL of the background task, which is used in the poll requests described below. The Location header contains an absolute URL, whereas the href attribute contains a relative URL.

 

Polling the background task

To poll the status of the upload task, the client makes the following requests:

GET /fotoweb/me/background-tasks/0735ca40-3a93-11e9-ae8d-34e12de8a773
Accept: application/vnd.fotoware.upload-status+json

If the upload task exists, the following response is returned:

200 OK
Content-Type: application/vnd.fotoware.upload-status.json

 

with the following request body:

{
  "job": {
    "status": "pending" | "inProgress" | "done" | "failed",
    "result": {
      "uploadedFiles": [
        {
          "href": "/fotoweb/archives/5000-Wirefeed/Incoming/image.jpg",
          "done": true | false,
          "originalFilename": "image.jpg",
          "status": "done" | "failed",
          "errorCode": "..." | null,
          "errorMessage": "..." | null,
          "asset": {...} | null,
        },
        {...},
        ...
      ]
    } | null,
    "updates": {
      "frequency": 100,
      "href": "/fotoweb/me/background-tasks/0735ca40-3a93-11e9-ae8d-34e12de8a773",
      "type": "replace"
    }
  },
  "task": {
    "status": "pending" | "inProgress" | "done" | "failed",
    "created": "2019-02-27T13:24:36.708Z",
    "modified": "2019-02-27T13:24:36.708Z",
    "href": "/fotoweb/me/background-tasks/0735ca40-3a93-11e9-ae8d-34e12de8a773",
    "type": "upload",
  }
}

If the background task does not exist, the response is:

404 Not Found

If the background task belongs to a different user than the requesting one, the response is:

403 Forbidden

where

Attribute

Type

Description

job.status Enum (string)

Status of the background task. Can have the following values:

  • pending: The task is queued for processing
  • inProgress: Processing is still in progress.
  • done: Processing has finished, and a result is available.
  • failed: Processing has failed, and a result is available.

See the discussion of error handling below.

result List of objects

Result of the background task.

List of uploaded files, their status, and their location in FotoWeb

Do NOT assume that the order of entries in this array is the same as in the upload request!

See the description of the originalFilename attribute.

This is null, unless job.status is one of done and failed.

result.asset Asset (Object)

Full API representation of the asset.

Its API URL, in asset.href, can be used for making further API requests to it (e.g., assign additional metadata).

This attribute may be null in some cases where it is unknown where the asset was uploaded to, particularly

  • If uploading to the user's upload area
  • If uploading to an archive with a custom input folder
  • If uploading the asset has failed.

The attribute represents the state of the asset at the time the upload has finished. If the asset's state (e.g., its metadata) has changed since, then this attribute may be out of date.

Request the asset representation of the asset using its API URL, asset.href, to get its latest state.

result.done Bool
  • true if file was successfully uploaded or has failed
  • false if upload is still in progress
result.errorCode String

Symbolic name of the type of error that has occurred uploading this asset.

Can be used for programmatically handling different errors in different ways.

Currently unspecified, reserved for future use.

This attribute is null if the asset has been uploaded successfully and job.status is failed.

This attribute is not present if job.status is done.

result.errorMessage String

Human-readable description of error that has occurred uploading this asset.

Can be displayed to a user.

This attribute is null if the asset has been uploaded successfully and job.status is failed.

This attribute is not present if job.status is done.

result.href URL (String) The final API URL of the uploaded asset. Can be used for making further API requests to it (e.g., assign additional metadata).
result.originalFilename String

The original filename of an uploaded file, as given in the upload request.

This allows the client to identify which file an entry in the result array applies to.

This is the filename that was specified in the upload request, with no modifications.

FotoWeb may change the filename during the upload, in order to generate a compliant and unique filename. The final filename of the file after the upload can be found in result.asset.filename.

task.created Date/time (ISO 8601 string) Time when task was created
task.modified Date/time (ISO 8601 string) Time when task status has last changed
task.type Enum (string) This is always "upload".
updates.frequency Integer

Recommended time to wait (in milliseconds) until making the next poll request.

updates.href URL (string)

URL of the background task for making the next polling request.

Although the background task URL does not change in the current implementation, clients MUST use this URL to make the next poll request.

updates.type Enum (string) This is always "replace".

 

Error handling

If the job.status attribute of an upload task result is done, then upload of ALL files has succeeded. The location of each file can be checked in the result attribute.

If the job.status attribute of an upload task result is failed,then upload of ONE OR MORE files has failed, but some files may have been uploaded successfully. The status of each file can be checked in the result attribute. If the errorCode attribute in an entry in result is null, then that file was uploaded successfully. Otherwise, errorCode and errorMessage contain further information about the error. Clients may retry to upload these files. The server guarantees that they were not transferred to their final storage (or that they have been deleted again).

If a poll request fails with a status code above 500, then clients should repeat polling with the same frequency. It is at the discretion of the client to stop polling or increase the interval in this case.

If a poll request fails with a status code of or above 400 and below 500, then clients should stop polling. Such errors are either unexpected or due to bugs in the client or server. An error message should be shown to the user, and the status of the upload is unknown.

User interface considerations

As long as an upload task is pending, the upload should be shown as in progress.

Progress bars are easy to calculate during file transfer (the initial upload request), but during polling / post processing, it is hard to know the current status, so a progress bar should show either 100% or something close to it (which is also the maximum of the file transfer process), such as 99%.

It is not possible to abort / cancel post processing. If the client simply stops polling, the status of the upload will be unknown.

It is OK to first complete all upload requests and poll the status of all uploads at the end. This makes most sense for a modal dialog (but it shows the status to the user only when all uploads have finished). A user interface that shows the status in real time and remains responsive all the time (background upload) should poll all the time.

Preserving the modification date/time of uploaded assets

When uploading assets via the API it may be desirable to preserve the original modification time of the assets being uploaded. To accomplish this, get the file modification date from the file to upload (e.g. using the file system API on your platform), then send it in the upload request using the "mt" attribute, as shown here:

{
  "fields": [... (patch instructions for metadata fields) ...],
  "attributes": [
    {"key": "mt", "value": "2018-01-02T11:22:33Z"}
  ]
}

where

Parameter

Description

key Specifies which attribute to set
  • "mt": File modification time
value Specifies the value of the attribute
  • "mt":  "2018-01-02T11:22:33Z"

Applying metadata during upload

Metadata can be applied to assets during upload. This ensures strong metadata consistency in FotoWeb archives. If a client uploaded assets and applied metadata later in a separate request, then these assets might be without metadata, without consistent metadata or without required metadata fields for a short period of time after the upload. Applying metadata during upload ensures that all assets in the FotoWeb systems always have fully consistent metadata.

Metadata can be applied to each asset in an upload request. This is done by adding a metadata part to the request body for each asset to which metadata shall be applied:

--WUG7zDvyA3hVq16Q
Content-Disposition: name="Metadata" filename="image.jpg.metadata.json"
 
{
  "fields": [
    {"id": 5, "value": "Roadrunner"},
    {"id": 80, "value": "Wyle E. Coyote", "action": "add"},
    {"id": 25, "action": "erase"},
    {"id": 25, "action": "add", "value": ["chicken", "food"]}
  ]
}

 where the Content-Disposition header parameter must be set as follows:

Parameter

Description

name Must always be "Metadata".
filename

Must be "FILENAME.EXT.metadata.json" where "FILENAME.EXT" is the filename of the file (including extension) as given in the filename parameter of the request and ".metadata.json" is a pseudo-extension that must always be the same.

For example, when applying metadata to an asset where filename is set to "image.jpg" in the "FileData" part, then the "Metadata" part must begin as follows:

--WUG7zDvyA3hVq16Q
Content-Disposition: name="Metadata" filename="image.jpg.metadata.json"

Metadata patch instructions

In the JSON body, fields is an array of metadata patch instructions where each instruction has the following parameters:

Parameter

Description

id

Numeric ID of the metadata field.

This parameter is required.

action

Metadata action to perform, as described below.

This parameter is optional. If omitted, the default action is "add".

value

Metadata value(s) to set or add.

This parameter is optional or required, depending on the action, as described below.

Depending on the action and field type, this can be a single value (given as a string) or zero or multiple values (given as an array of strings).

The action parameter defines what the metadata patch instruction does:

Action

Description

"add"

Set the field to the given value or values.

If the field already has a value, and the field is a bag field, then the value is added to the existing values of the field.

The value parameter is required. If the field is a bag field, then either a single value or multiple values may be given. Otherwise, at most one value may be given (either as a string or as an array containing a single string element). If an empty array is given, then this action does nothing.

Example:

// If the field is a non-bag field:
"value": "foo"           // Replaces existing value with "foo"
"value": ["foo"]         // Replaces existing value with "foo"
"value": []              // valid, but does nothing
"value": ["foo", "bar"]  // INVALID, because at most one value may be given


// If the field is a bag field:
"value": "foo"           // Adds "foo" to existing values
"value": ["foo"]         // Adds "foo" to existing values
"value": ["foo", "bar"]  // Adds "foo" and "bar" to existing values
"value": []              // valid, but does nothing
"append"

Append the given value to the existing value of the field. If the field does not have a value, then this action is equivalent to "add".

If the field is a bag field, then the given value is appended to the first value of the existing array of values of the field.

The value parameter is required and must be a string containing a single value.

"prepend"

Prepend the given value to the existing value of the field. If the field does not have a value, then this action is equivalent to "add".

If the field is a bag field, then the given value is prepended to the first value of the existing array of values of the field.

The value parameter is required and must be a string containing a single value.

"erase"

Remove the given field from the metadata.

If the field is a bag field, then all values in the bag are removed.

 

Example: Applying metadata to non-bag fields

The following metadata patch instructions:

{
  "fields": [
    {"id": 500, "value": "V1"},
    {"id": 501, "action": "erase"},
    {"id": 502, "action": "append", "value": "V3"},
    {"id": 503, "action": "prepend", "value": "V4"}
  ]
}

modifies the following existing metadata as follows:

Field

Existing value

Final value

500 E1 V1
501 E2 (empty)
502 E3 E3V3
503 E4 V4E4

 

Example: Applying metadata to a bag field

The following metadata patch instructions:

{
  "fields": [
    {"id": 25, "action": "erase"},
    {"id": 25, "action": "add", "value": ["food", "chicken"]},
    {"id": 80, "action": "add", "value": "Wyle E. Coyote"}
  ]
}

modifies the following existing metadata as follows:

Field

Existing values

Final values

25

foo

bar

food

chicken

80 Roadrunner

Roadrunner

Wyle E. Coyote

Note: ​ To replace all values in a bag field with new values, use the "erase" action before adding new values with the add action.

Metadata fields that are automatically set

When uploading assets to FotoWeb, FotoWeb sets certain fields automatically. This is described here.

Full example

The following is an example of a complete upload request with metadata:

POST /fotoweb/archives/5000-Wirefeed/Incoming/
Accept: application/vnd.fotoware.upload-response+json
Content-Type: multipart/form-data; boundary=WUG7zDvyA3hVq16Q
authentication headers
 
--WUG7zDvyA3hVq16Q
Content-Disposition: form-data; name="Filedata"; filename="image.jpg"
Content-Type: image/jpeg
 
binary image data
--WUG7zDvyA3hVq16Q
Content-Disposition: name="Metadata" filename="image.jpg.metadata.json"
 
{
  "fields": [
    {"id": 5, "value": "Roadrunner"},
    {"id": 80, "value": "Wyle E. Coyote", "action": "add"},
    {"id": 25, "action": "erase"},
    {"id": 25, "action": "add", "value": ["chicken", "food"]}
  ]
}
--WUG7zDvyA3hVq16Q
  • Was this article helpful?