Methods for using pixeldrain programmatically.
All methods which create, modify or delete a resource require an API key. API keys can be obtained from your user account’s API keys page.
To use the API key you need to enter it in the password field of HTTP Basic Access Authentication. The username field does not matter, it can be empty or anything else.
Example usage in JavaScript:
const resp = await fetch(
"https://pixeldrain.com/api/user/files",
headers: {
"Authorization": "Basic "+btoa(":"+api_key),
// The btoa function encodes the key to Base64
},
)
if(resp.status >= 400) {
throw new Error(await resp.json())
}
result = await resp.json()
Some JSON responses include fields which end in “_href” (some people don’t know this, but “href” stands for “Hypertext Reference”, the more you know). These point to different places in the API, which you can retrieve with a GET request. The path is to be appended to the API URL, so “/file/someid/thumbnail” becomes “/api/file/someid/thumbnail”.
The base URL for the API is “/api”, all paths below are relative to that URL.
To upload files to pixeldrain you will need an API key. Get an API key from the API keys page and enter it in the command. Replace the example API key here with your own:
curl -T "file_name.txt" -u :5f45f184-64bb-4eaa-be19-4a5f0459db49
https://pixeldrain.com/api/file/
Every API endpoint which does not return raw file data responds with JSON. A
JSON response always contains a success field. When a request fails (the HTTP
status code is 400 or higher) the response also contains a value field with a
machine-readable error code and a message field with a human-readable
explanation of what went wrong:
HTTP 404: Not Found
{
"success": false,
"value": "not_found",
"message": "The entity you requested could not be found"
}
The message text can change between versions of the API, the value code is
stable. Use value when handling errors programmatically.
Some errors contain an extra object with additional information about the
error, like which form field caused it:
HTTP 400: Bad Request
{
"success": false,
"value": "missing_field",
"message": "A required field was not sent with this request. See extra for more information",
"extra": {
"field": "name"
}
}
Endpoints which validate multiple form fields can return more than one error in
a single response. In that case the value is multiple_errors and the
response contains an errors array. Each element of the array is a normal
error object as described above:
HTTP 400: Bad Request
{
"success": false,
"value": "multiple_errors",
"message": "This API endpoint can return multiple errors which need to be checked individually. See the errors array",
"errors": [
{
"success": false,
"value": "string_out_of_range",
"message": "A string parameter you passed was either too short or too long",
"extra": {
"field": "name",
"min_len": 1,
"max_len": 255,
"len": 300
}
},
{
"success": false,
"value": "missing_field",
"message": "A required field was not sent with this request. See extra for more information",
"extra": {
"field": "file"
}
}
]
}
The HTTP status of a multiple_errors response is 400, unless one of the contained errors is a server error, then it’s elevated to 500.
These error codes can be returned by any API endpoint. Errors which only apply to a specific endpoint are documented with that endpoint.
| Value | HTTP status | Description |
|---|---|---|
| internal | 500 | Something went wrong on the server side, try again later |
| api_not_implemented | 501 | This API does not exist yet |
| not_found | 404 | The entity you requested could not be found |
| invalid_endpoint | 404 | The API endpoint you requested does not exist, check the URL for errors |
| resource_already_exists | 400 | The resource could not be created because it already exists |
| websocket_endpoint | 400 | This is a websocket endpoint, pass the ‘Upgrade: websocket’ header to upgrade the connection |
| ip_rate_limit_reached | 429 | Your IP address has reached a limit for this action, try again later |
| network_error | 400 | An error occurred while transmitting the response |
| read_only_mode_enabled | 503 | The server is in read-only mode, writing is temporarily disabled |
| multiple_errors | 400 / 500 | Multiple errors occurred, see the errors array in the response |
These errors are returned when a request contains invalid input. Where possible
the extra object contains a field key with the name of the offending form
field.
| Value | HTTP status | Description |
|---|---|---|
| missing_field | 400 | A required field was not sent with the request, see extra.field |
| json_parse_failed | 422 | The JSON object in the request body could not be parsed |
| base64_parse_failed | 422 | The base64 object in the request body could not be parsed |
| bool_parse_failed | 422 | Failed to parse a boolean value, allowed values are ‘true’ and ‘false’ |
| integer_parse_failed | 422 | An integer parameter could not be parsed |
| integer_out_of_range | 422 | An integer parameter was either too high or too low, see extra.min_val and extra.max_val |
| string_out_of_range | 422 | A string parameter was either too short or too long, see extra.min_len and extra.max_len |
| utf8_string_parse_failed | 422 | A string parameter contains invalid UTF-8 data |
| time_parse_failed | 422 | A time parameter was not formatted correctly, time should be in RFC 3339 format |
| enum_parse_failed | 422 | A string parameter did not contain an allowed value, see extra.allowed_values |
| uuid_parse_failed | 422 | A UUID parameter was not formatted correctly |
| hex_parse_failed | 422 | A hexadecimal parameter was not formatted correctly |
| address_parse_failed | 422 | A network address parameter was not formatted correctly |
| error_reading_input | 400 | The values you sent could not be read on the server side, try again |
| incorrect_order_of_form_parts | 400 | This API requires form parameters to be ordered the way they are documented |
| recpatcha_failed | 424 | The captcha response could not be verified |
| invalid_email_address | 400 | The e-mail address entered is not valid |
| field_contains_illegal_character | 400 | A string parameter contains a disallowed character, see extra.char and extra.char_position |
| invalid_file_type | 400 | The file you submitted is not of the right type |
| invalid_x509_keypair | 400 | Failed to parse the provided x509 keypair |
| Value | HTTP status | Description |
|---|---|---|
| authentication_required | 401 | This request requires an API key, see the Authentication chapter |
| authentication_failed | 401 | The provided API key is invalid, has been revoked or has expired |
| forbidden | 403 | You are not authorized to execute this request |
| user_not_found | 404 | User with this name or e-mail address does not exist |
| password_incorrect | 400 | The entered password is not correct for this user |
| otp_incorrect | 400 | The entered one-time password is not correct for this user |
| otp_required | 400 | A one-time password is required to log in to this account |
| Value | HTTP status | Description |
|---|---|---|
| no_file | 422 | The file does not exist or is empty |
| file_too_large | 413 | The file you tried to upload is larger than your plan allows |
| writing | 500 | Something went wrong while writing the file to disk, the server may be out of storage space |
| name_contains_illegal_character | 422 | The file name contains characters which are not allowed in file names, see extra.illegal_chars |
| invalid_content_type | 400 | You sent multipart form data to the PUT endpoint, use the POST endpoint for form uploads |
| ip_banned | 403 | This IP address has been banned from using pixeldrain for violating the content policy |
| account_banned | 403 | This user account has been banned from using pixeldrain for violating the content policy |
| user_out_of_space | 400 | Your account has run out of storage space, upgrade to a higher plan to continue uploading |
| too_many_files | 400 | You have too many files on your account, delete some old files or switch to the filesystem |
| Value | HTTP status | Description |
|---|---|---|
| file_rate_limited_captcha_required | 403 | Hotlinking was detected for this file, a captcha is required to continue downloading. The captcha entry is available on the download page |
| virus_detected_captcha_required | 403 | This file has been marked as malware, a captcha is required to download it. The captcha entry is available on the download page |
| hotlink_detected | 403 | Hotlinking was detected for this download, hotlinking is only allowed on premium accounts |
| ip_download_limited_captcha_required | 403 | Your IP address made too many download requests in the last 24 hours, wait or complete a captcha |
| server_overload_captcha_required | 403 | The servers are under too much stress, free users are asked to complete a captcha before starting new downloads |
| max_concurrent_downloads | 403 | You have reached the maximum number of open download connections for free accounts |
| transfer_limit_exceeded | 403 | You have exceeded the transfer limit for free users, upgrade to a premium plan to continue downloading |
| download_limit_exceeded | 403 | You have exceeded the download limit for free users, upgrade to a premium plan to continue downloading |
| unavailable_for_legal_reasons | 451 | This file cannot be downloaded because it has received a takedown report, see extra.type for the report type |
| invalid_referrer | 400 | The URL in the Referer header could not be parsed |
| embed_not_allowed | 403 | Embedding this file is not allowed |
| Value | HTTP status | Description |
|---|---|---|
| not_enough_credit_for_prepaid | 400 | To activate a prepaid subscription you need at least €1 on your account |
| cannot_disable_patreon_sub | 400 | A Patreon subscription can only be disabled through Patreon itself |
Upload a file. I recommend that you use the PUT API instead of the POST API. It’s easier to use and the multipart encoding of the POST API can cause performance issues in certain environments.
This API requires authentication with an API key. Anonymous uploading is not supported.
| Param | Type | Required | Maximum Size | Default | Description |
|---|---|---|---|---|---|
| name | string | false | 255 characters | multipart file name | Name of the file to upload |
| file | multipart file | true | Depends on user subscription | none | File to upload |
HTTP 201: Created
{
"success": true,
"id": "abc123" // ID of the newly uploaded file
}
Example error:
HTTP 422: Unprocessable Entity
{
"success": false,
"value": "no_file",
"message": "The file does not exist or is empty"
}
| Value | HTTP status | Description |
|---|---|---|
| authentication_required | 401 | No API key was provided, uploading requires an account |
| no_file | 422 | The file does not exist or is empty |
| file_too_large | 413 | The file is larger than your plan’s file size limit |
| missing_field | 400 | The file has no name, see extra.field |
| string_out_of_range | 422 | The file name is longer than 255 characters |
| name_contains_illegal_character | 422 | The file name contains characters which are not allowed, see extra.illegal_chars |
| error_reading_input | 400 | The upload was interrupted before it completed |
| too_many_files | 400 | Your account holds more files than the per-account limit |
| user_out_of_space | 400 | Your account has run out of storage space |
| ip_banned | 403 | Your IP address has been banned for violating the content policy |
| account_banned | 403 | Your account has been banned for violating the content policy |
| writing | 500 | Failed to write the file to disk, the server may be out of storage space |
| internal | 500 | An internal server error occurred |
Upload a file. The file should be sent as the raw request body, do not use multipart form encoding here. If you want to use form encoding use the POST API instead.
This API requires authentication with an API key. Anonymous uploading is not supported.
| Param | Type | Required | Location | Maximum Size | Default | Description |
|---|---|---|---|---|---|---|
| name | string | true | URL | 255 characters | none | Name of the file to upload |
| file | file | true | request body | Depends on user subscription | none | File to upload |
On success the full file info is returned, this is the same structure as the GET /file/{id}/info response.
HTTP 201: Created
{
"success": true,
"id": "abc123", // ID of the newly uploaded file
"name": "file_name.txt",
"size": 123456,
... // Remaining file info fields, see GET /file/{id}/info
}
Example error:
HTTP 413: Payload Too Large
{
"success": false,
"value": "file_too_large",
"message": "The file you tried to upload is too large"
}
| Value | HTTP status | Description |
|---|---|---|
| authentication_required | 401 | No API key was provided, uploading requires an account |
| invalid_content_type | 400 | You sent multipart form data, use the POST endpoint for form uploads |
| file_too_large | 413 | The file is larger than your plan’s file size limit |
| missing_field | 400 | The file has no name, see extra.field |
| string_out_of_range | 422 | The file name is longer than 255 characters |
| name_contains_illegal_character | 422 | The file name contains characters which are not allowed, see extra.illegal_chars |
| error_reading_input | 400 | The upload was interrupted before it completed |
| too_many_files | 400 | Your account holds more files than the per-account limit |
| user_out_of_space | 400 | Your account has run out of storage space |
| ip_banned | 403 | Your IP address has been banned for violating the content policy |
| account_banned | 403 | Your account has been banned for violating the content policy |
| writing | 500 | Failed to write the file to disk, the server may be out of storage space |
| internal | 500 | An internal server error occurred |
Returns the full file associated with the ID. Supports byte range requests.
You can also download multiple files at once by separating the IDs with commas in the URL. The files will then be served as a zip archive.
When ‘?download’ is added to the URL the server will send an attachment header instead of inline rendering, which causes the browser to show a ‘Save File’ dialog.
Warning: If a file is using too much bandwidth it can be rate limited. The rate limit will be enabled if a file has three times more downloads than views. The owner of a file can always download it. When a file is rate limited the user will need to fill out a captcha in order to continue downloading the file. The captcha will only appear on the file viewer page (pixeldrain.com/u/{id}). Rate limiting has been added to prevent the spread of viruses and to stop hotlinking. Hotlinking is only allowed when either the uploader or the downloader has a premium subscription.
Free downloads are also subject to limits on the number of downloads, the amount of data transferred and the number of simultaneous connections per IP address. When one of these limits is hit the download fails with one of the errors listed below. Most of these restrictions can be lifted by completing a captcha on the download page, or by getting a premium subscription.
| Param | Required | Location | Description |
|---|---|---|---|
| id | true | URL | ID of the file to request |
| download | false | URL | Sends attachment header instead of inline |
| recaptcha_response | false | URL | Captcha response token, lifts captcha-based download restrictions |
HTTP 200: OK
Requested file data
Example error:
HTTP 403: Forbidden
{
"success": false,
"value": "file_rate_limited_captcha_required",
"message": "We have detected the use of hotlinking for this file. Hotlinking is only supported when either the uploader or the downloader has a paid subscription"
}
| Value | HTTP status | Description |
|---|---|---|
| not_found | 404 | The file does not exist. For multi-file requests extra.file contains the ID which was not found |
| file_rate_limited_captcha_required | 403 | The file is rate limited, complete the captcha on the download page to continue |
| virus_detected_captcha_required | 403 | The file has been marked as malware, complete the captcha on the download page to continue |
| hotlink_detected | 403 | Hotlinking was detected, hotlinking is only allowed with a premium subscription |
| ip_download_limited_captcha_required | 403 | Your IP address made too many download requests in the last 24 hours, wait or complete a captcha |
| max_concurrent_downloads | 403 | Too many simultaneous download connections from your IP address, wait for other downloads to finish |
| transfer_limit_exceeded | 403 | You have exceeded the free transfer limit, upgrade to premium to continue downloading |
| download_limit_exceeded | 403 | You have exceeded the free download limit, upgrade to premium to continue downloading |
| unavailable_for_legal_reasons | 451 | The file received a takedown report and cannot be downloaded, see extra.type |
| recpatcha_failed | 424 | The submitted captcha response token could not be verified |
| internal | 500 | An internal server error occurred |
Returns information about one or more files. You can also put a comma separated list of file IDs in the URL and it will return an array of file info, instead of a single object. There’s a limit of 100 files per request. Files which do not exist are left out of the array, the request only fails with not_found if none of the requested files exist.
| Param | Required | Location | Description |
|---|---|---|---|
| id | true | URL | ID of the file |
HTTP 200: OK
{
"success": true,
"id": "1234abcd",
"name": "screenshot.png",
// Size of the file in bytes
"size": 5694837,
// Number of unique file views, views are counted once per IP address
"views": 1234,
// Total bandwidth usage of the file
"bandwidth_used": 1234567890,
// Premium bandwidth usage, from users with a Pro subscription or bandwidth sharing
"bandwidth_used_paid": 1234567890,
// Unique downloads per IP address
"downloads": 1234,
"date_upload": "2020-02-04T18:34:05.706801Z",
"date_last_view": "2020-02-04T18:34:05.706801Z",
"mime_type": "image/png",
// Link to a thumbnail of this file
"thumbnail_href": "/file/1234abcd/thumbnail",
// SHA256 sum of the file, encoded in hexadecimal
"hash_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
// When the delete_after_date or delete_after_downloads options are used
// these fields show when the file will be deleted
"delete_after_date": "0001-01-01T00:00:00Z",
"delete_after_downloads": 0,
// Availability indicates whether downloading is restricted. It's empty if
// the file can be downloaded normally, else it contains one of the file
// download error codes and availability_message contains an explanation
"availability": "",
"availability_message": "",
// If the file received an abuse report these fields contain the type of
// report and the name of the reporting organization
"abuse_type": "",
"abuse_reporter_name": "",
// If the current logged in user can edit or download the file
"can_edit": true,
"can_download": true,
// Whether advertisements are shown on the download page of this file
"show_ads": false,
// Whether the video player is available for this file
"allow_video_player": true,
// Download speed limit in bytes per second, 0 means no limit
"download_speed_limit": 0
}
| Value | HTTP status | Description |
|---|---|---|
| not_found | 404 | None of the requested files exist |
| string_out_of_range | 422 | More than 100 file IDs were requested at once |
| internal | 500 | An internal server error occurred |
Returns a PNG thumbnail image representing the file. The thumbnail image will be 256x256 px by default. You can specify the width and height with parameters in the URL. The width and height parameters need to be a multiple of 16 between 16 and 256. So the allowed values are 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240 and 256. If a thumbnail cannot be generated for the file you will get an image representing the type of the file instead.
| Param | Required | Location | Description |
|---|---|---|---|
| id | true | URL | ID of the file to get a thumbnail for |
| width | false | URL | Width of the thumbnail image |
| height | false | URL | Height of the thumbnail image |
A PNG image if a thumbnail can be generated. If a thumbnail cannot be generated you will get an icon representing the mime type of the file instead.
| Value | HTTP status | Description |
|---|---|---|
| not_found | 404 | The file does not exist |
| invalid_thumbnail_size | 400 | The width or height is not a multiple of 16 between 16 and 256 |
| internal | 500 | An internal server error occurred |
Deletes a file. Only works when the users owns the file.
| Param | Required | Location | Description |
|---|---|---|---|
| id | true | URL | ID of the file to delete |
HTTP 200: OK
{
"success": true,
"value": "ok",
"message": "The requested action was successfully performed"
}
| Value | HTTP status | Description |
|---|---|---|
| not_found | 404 | The file does not exist |
| forbidden | 403 | You are not logged in or this is not your file |
| internal | 500 | An internal server error occurred |
Creates a list of files that can be viewed together on the file viewer page.
POST body should be a JSON object, example below. A list can contain at most 10000 files. If you try to add more the request will fail.
{
"title": "My beautiful photos", // Defaults to "Pixeldrain List"
"anonymous": false / true, // If true this list will not be linked to your user account. Defaults to "false"
"files": [ // Ordered array of files to add to the list
{
"id": "abc123"
},
{
"id": "123abc"
}
]
}
HTTP 201: Created
{
"success": true,
"id": "yay137" // ID of the newly created list
}
Example error:
HTTP 422: Unprocessable Entity
{
"success": false,
"value": "list_file_not_found",
"message": "File was not found in the database",
"extra": {
"file_not_found": "0h42No" // The file you tried to add with this ID does not exist
}
}
| Value | HTTP status | Description |
|---|---|---|
| json_parse_failed | 422 | The JSON object in the request body could not be parsed |
| list_file_not_found | 422 | One of the files in the list does not exist, see extra.file_not_found |
| too_many_files | 413 | The list contains more than 10000 files |
| title_too_long | 413 | The title of the list is longer than 300 characters |
| cannot_create_empty_list | 422 | You cannot make a list with no files |
| internal | 500 | An internal server error occurred |
Returns information about a file list and the files in it.
| Param | Required | Location | Description |
|---|---|---|---|
| id | true | URL | ID of the list |
The API will return some basic information about every file. Every file also has a “detail_href” field which contains a URL to the info API of the file. Follow that link to get more information about the file like size, checksum, mime type, etc. The address is relative to the API URL and should be appended to the end.
HTTP 200: OK
{
"success": true,
"id": "L8bhwx",
"title": "Rust in Peace",
"date_created": "2020-02-04T18:34:13.466276Z",
"file_count": 3,
// If the current logged in user can edit the list
"can_edit": false,
"files": [
// These structures are the same as the file info response, except for
// the detail_href and description fields
{
"detail_href": "/file/_SqVWi/info",
"description": "",
"success": true,
"id": "_SqVWi",
"name": "01 Holy Wars... The Punishment Due.mp3",
"size": 123456,
"date_upload": "2020-02-04T18:34:13.466276Z",
"date_last_view": "2020-02-04T18:34:13.466276Z",
"mime_type": "audio/mp3",
"views": 1,
"bandwidth_used": 1234567890,
"thumbnail_href": "/file/_SqVWi/thumbnail"
},
{
"detail_href": "/file/RKwgZb/info",
"description": "",
"success": true,
"id": "RKwgZb",
"name": "02 Hangar 18.mp3",
"size": 123456,
"date_upload": "2020-02-04T18:34:13.466276Z",
"date_last_view": "2020-02-04T18:34:13.466276Z",
"mime_type": "audio/mp3",
"views": 2,
"bandwidth_used": 1234567890,
"thumbnail_href": "/file/RKwgZb/thumbnail"
},
{
"detail_href": "/file/DRaL_e/info",
"description": "",
"success": true,
"id": "DRaL_e",
"name": "03 Take No Prisoners.mp3",
"size": 123456,
"date_upload": "2020-02-04T18:34:13.466276Z",
"date_last_view": "2020-02-04T18:34:13.466276Z",
"mime_type": "audio/mp3",
"views": 3,
"bandwidth_used": 1234567890,
"thumbnail_href": "/file/DRaL_e/thumbnail"
}
]
}
HTTP 404: Not Found
{
"success": false,
"value": "not_found",
"message": "The entity you requested could not be found"
}
These methods are used to interact with the pixeldrain filesystem. The filesystem is available to accounts with a paid subscription.
Every filesystem endpoint works on a path, and every path starts with a bucket. There are two ways to address a bucket:
me is your own filesystem, this requires authentication with an API key.
Example: /filesystem/me/documents/todo.txthttps://pixeldrain.com/d/abcd1234.
Example: /filesystem/abcd1234/photos/cat.jpgSpecial characters in path components need to be URL-encoded.
The owner of a filesystem can always read and write. What other users are
allowed to do is determined by the permissions configured on the shared
directory with the update action documented below.
Information about files and directories is returned as a ‘node’ object. This is what a node looks like:
{
// Type is either "file" or "dir"
"type": "file",
"path": "/documents/todo.txt",
"name": "todo.txt",
"created": "2024-02-04T18:34:13.466276Z",
"modified": "2024-02-04T18:34:13.466276Z",
// Unix file mode in symbolic and octal form
"mode_string": "-rw-r--r--",
"mode_octal": "644",
// Username of the account which created the node
"created_by": "some_user",
// Only present on files
"file_size": 9757269,
"file_type": "text/plain",
"sha256_sum": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
// Only present if the node, or one of its parents, is shared. This is the
// ID which can be used to access the node without authentication
"id": "abcd1234",
// Only present if the node received an abuse report. Nodes with an abuse
// type cannot be downloaded
"abuse_type": "",
"abuse_report_time": "2024-02-04T18:34:13.466276Z",
// Branding properties configured with the update action, only present if
// branding is configured
"properties": {
"branding_enabled": "true",
"brand_header_image": "abcd1234"
},
// Sharing permissions, only visible to the owner of the filesystem
"link_permissions": {"owner": false, "read": true, "write": false, "delete": false},
"user_permissions": {"friendly_user": {"owner": false, "read": true, "write": true, "delete": false}},
"password_permissions": {"hunter2": {"owner": false, "read": true, "write": false, "delete": false}}
}
Reads a file or directory. If the path is a file the file contents are returned, byte range requests are supported. If the path is a directory a stat response is returned, see below.
This endpoint also has a collection of sub-APIs which can be accessed by adding query parameters to the URL:
| Param | Description |
|---|---|
| attach | Sends an attachment header instead of inline rendering, which causes the browser to show a ‘Save File’ dialog |
| stat | Returns information about the node instead of the file contents, see the stat response below |
| thumbnail | Returns a PNG thumbnail of the file. Use the width and height parameters to set the size, multiples of 16 between 16 and 256, default is 256 |
| bulk_download | Downloads the directory and everything in it as a zip archive |
| search | Searches the shared directory for files matching the search term. The term goes in the parameter value: ?search=term. The term needs to be between 2 and 100 characters. Use limit to set the number of results, between 1 and 1000, default is 10. Returns an array of matching paths |
| torrent_info | Returns the parsed metadata of a torrent file. The file needs to be of type application/x-bittorrent and at most 16 MiB large |
| zip_info | Returns the file listing of a zip archive |
| zip_file | Extracts a single file from a zip or 7z archive and returns its contents. The path of the file within the archive goes in the parameter value: ?zip_file=/dir/file.txt |
| render_markdown | Renders a markdown file to HTML. The file needs to be of type text/markdown or text/plain and at most 2 MiB large |
| download_stats | Returns the total download and transfer statistics of the node. Can also be upgraded to a websocket connection which streams updated statistics in real time |
| timeseries | Returns historic download and transfer statistics. Requires the start and end parameters in RFC 3339 format and interval in minutes between 1 and 1440 |
| change_log | Returns the activity log of a directory which has logging_enabled set. Requires the start and end parameters in RFC 3339 format, up to 30 days at a time |
When the path is a file and no query parameters are used the raw file contents are returned.
When the path is a directory, or the stat parameter is used, a stat object is
returned. The path array contains a node object (described at the top of this
chapter) for every path component, so you can show a breadcrumb navigation bar
with it. base_index is the index of the requested node in the path array.
children contains the nodes inside the directory:
HTTP 200: OK
{
"path": [
{
"type": "dir",
"path": "/",
"name": "some_dir",
... // Remaining node fields
}
],
"base_index": 0,
"children": [
{
"type": "file",
"path": "/todo.txt",
"name": "todo.txt",
... // Remaining node fields
}
],
// The permissions you have on this path
"permissions": {
"owner": false,
"read": true,
"write": false,
"delete": false
},
"context": {
// Whether downloads from this filesystem use premium transfer
"premium_transfer": true
}
}
| Value | HTTP status | Description |
|---|---|---|
| not_found | 404 | The bucket in the first path component does not exist |
| path_not_found | 404 | No file or directory exists at this path |
| authentication_required | 401 | The me bucket was requested without an API key |
| permission_denied | 400 | You do not have read permission on this path |
| node_is_a_directory | 400 | A file operation was requested on a directory |
| node_is_not_a_directory | 400 | A directory operation was requested on a file |
| unavailable_for_legal_reasons | 451 | The file received a takedown report and cannot be downloaded, see extra.abuse_type |
| embed_not_allowed | 403 | Embedding files from this filesystem is not allowed |
| invalid_referrer | 400 | The URL in the Referer header could not be parsed |
| not_a_torrent | 400 | The torrent_info target is not a torrent file |
| torrent_too_large | 400 | The torrent_info target is larger than 16 MiB |
| torrent_parse_failed | 400 | The torrent file could not be parsed |
| not_a_zip | 400 | The zip_info or zip_file target is not a zip or 7z archive |
| invalid_zip_file | 400 | The zip archive could not be parsed |
| unsupported_zip_compression | 400 | The zip archive uses a compression method which is not supported |
| not_a_markdown_file | 400 | The render_markdown target is not a markdown or plain text file |
| file_too_large | 413 | The render_markdown target is larger than 2 MiB |
| search_not_a_directory | 400 | The search target is not a directory |
| search_term_too_short | 400 | The search term is shorter than 2 characters |
| search_term_too_long | 400 | The search term is longer than 100 characters |
| log_timespan_too_long | 400 | The change_log time span is longer than 30 days |
| internal | 500 | An internal server error occurred |
Writes a file to the filesystem. The file is sent as the raw request body, do not use multipart form encoding. If a file already exists at the path it is overwritten. Parameters are sent as URL query parameters.
| Param | Type | Required | Default | Description |
|---|---|---|---|---|
| path | string | true | none | Path to store the file at, the file name is the last path component |
| make_parents | boolean | false | false | Create the parent directories of the path if they don’t exist |
| mode | octal | false | 644 | Unix file mode of the new file |
Besides these, all the node properties from the update action of the POST
endpoint can be set with query parameters as well.
On success the node object of the new file is returned.
HTTP 200: OK
{
"type": "file",
"path": "/documents/todo.txt",
"name": "todo.txt",
... // Remaining node fields
}
| Value | HTTP status | Description |
|---|---|---|
| not_found | 404 | The bucket in the first path component does not exist |
| path_not_found | 404 | The parent directory does not exist and make_parents is not enabled |
| authentication_required | 401 | The me bucket was requested without an API key |
| permission_denied | 400 | You do not have write permission on this path |
| invalid_content_type | 400 | You sent multipart form data, send the file as the raw request body instead |
| error_reading_input | 400 | The upload was interrupted before it completed |
| name_too_long | 422 | The file name is longer than 255 bytes |
| path_too_long | 422 | The resulting path would be longer than 4095 bytes |
| too_many_nested_levels | 400 | The path contains more than 64 nested directories |
| too_many_children | 400 | The target directory has too many files |
| node_is_not_a_directory | 400 | One of the parent path components is a file |
| user_out_of_space | 400 | The filesystem owner’s account has run out of storage space |
| out_of_transfer | 400 | The filesystem owner has run out of premium data transfer |
| internal | 500 | An internal server error occurred |
Modifies the filesystem. The form parameter action selects the operation to
perform. Parameters are sent as form values, either URL-encoded or multipart.
Creates a new directory at the path. mkdir requires the parent directory to
exist, mkdirall also creates the missing parent directories.
| Param | Type | Required | Default | Description |
|---|---|---|---|---|
| action | enum | true | none | ‘mkdir’ or ‘mkdirall’ |
| mode | octal | false | 700 | Unix file mode of the new directory |
HTTP 201: Created
{
"success": true,
"value": "created",
"message": "The submitted resource was created"
}
Moves or renames a node. Note that the target path must include the bucket as
the first path component, like /me/documents/new_name.txt.
| Param | Type | Required | Default | Description |
|---|---|---|---|---|
| action | enum | true | none | ‘rename’ |
| target | string | true | none | The new path of the node, including the bucket |
| make_parents | boolean | false | false | Create the parent directories of the target path if they don’t exist |
On success the node object of the renamed node is returned.
Changes the metadata of a node. Only the parameters which are sent with the request are updated. On success the updated node object is returned.
| Param | Type | Description |
|---|---|---|
| action | enum | ‘update’ |
| created | time | Creation time of the node, RFC 3339 format |
| modified | time | Modification time of the node, RFC 3339 format |
| mode | octal | Unix file mode |
| shared | boolean | When set to true the node gets a public ID which can be used to access it without an account. When set to false the sharing link is disabled |
| logging_enabled | boolean | Enables the activity log on a directory, see the change_log API. Logging is automatically disabled again 24 hours after the log was last read |
| link_permissions | JSON | The permissions for people who open the sharing link, example: {"read": true, "write": false, "delete": false} |
| user_permissions | JSON | Map of pixeldrain usernames to permission objects, these users get access to the node when logged in |
| password_permissions | JSON | Map of passwords to permission objects |
| custom_domain_name | string | Custom domain name to serve this node on, requires a paid plan with the custom domain feature |
| custom_domain_cert | string | TLS certificate for the custom domain, PEM encoded |
| custom_domain_key | string | TLS private key for the custom domain, PEM encoded |
There are also branding parameters which can be used to customize the look of
shared directories: branding_enabled (‘true’ or ‘false’),
brand_input_color, brand_highlight_color, brand_danger_color,
brand_background_color, brand_body_color, brand_card_color (all
hexadecimal color codes like ‘#ff0000’), brand_header_image,
brand_background_image (IDs of image files in the same filesystem) and
brand_header_link (URL to open when the header is clicked). Sending an empty
value removes the property.
The permissions parameters can only be changed by the owner of the filesystem.
Copies files from your pixeldrain file list to a directory in the filesystem.
| Param | Type | Required | Description |
|---|---|---|---|
| action | enum | true | ‘import’ |
| files | JSON | true | JSON array of file IDs to import, example: ["abc123", "123abc"] |
HTTP 201: Created
{
"success": true,
"value": "created",
"message": "The submitted resource was created"
}
| Value | HTTP status | Description |
|---|---|---|
| not_found | 404 | The bucket in the first path component does not exist |
| path_not_found | 404 | No file or directory exists at this path |
| authentication_required | 401 | The me bucket was requested without an API key |
| permission_denied | 400 | You do not have write permission on this path |
| invalid_action | 400 | The action parameter is not one of the documented actions |
| cannot_modify_root_dir | 400 | The root directory of a filesystem cannot be modified |
| node_already_exists | 400 | A file or directory with this name already exists |
| name_too_long | 422 | The new name is longer than 255 bytes |
| path_too_long | 422 | The resulting path would be longer than 4095 bytes |
| too_many_nested_levels | 400 | The path contains more than 64 nested directories |
| too_many_children | 400 | The target directory has too many files |
| node_is_not_a_directory | 400 | The operation requires a directory but the path is a file |
| circular_dependency | 400 | A directory cannot be moved into itself |
| user_not_found | 404 | A username in user_permissions does not exist |
| invalid_x509_keypair | 400 | The custom domain certificate and key could not be parsed |
| list_file_not_found | 422 | One of the file IDs in the import action does not exist |
| json_parse_failed | 422 | A JSON parameter could not be parsed |
| internal | 500 | An internal server error occurred |
Deletes a file or directory. Directories can only be deleted when they are
empty, unless the recursive parameter is used.
| Param | Required | Location | Description |
|---|---|---|---|
| path | true | URL | Path of the node to delete |
| recursive | false | URL | Also delete the contents of a directory |
HTTP 200: OK
{
"success": true,
"value": "ok",
"message": "The requested action was successfully performed"
}
| Value | HTTP status | Description |
|---|---|---|
| not_found | 404 | The bucket in the first path component does not exist |
| path_not_found | 404 | No file or directory exists at this path |
| authentication_required | 401 | The me bucket was requested without an API key |
| permission_denied | 400 | You do not have delete permission on this path |
| directory_not_empty | 400 | The directory is not empty and recursive is not enabled |
| internal | 500 | An internal server error occurred |
These methods all require authentication with an API key, except for POST /user/login which is how you get an API key in the first place.
Because authentication is required, every endpoint in this chapter can return the authentication_required (401), authentication_failed (401) and internal (500) errors. These are not repeated in the error listings below.
Logs in to an account. On success a new session is created and the API key for the session is returned. Use this API key for all other user methods. The key is also set as a cookie named ‘pd_auth_key’, so browsers are logged in immediately.
There are two ways to log in:
username and password fields. The
username field accepts both a username and an e-mail address.username field with an e-mail address as
value and leave the password empty. A login link is e-mailed to the address,
the response will be login_link_sent (HTTP 202). The link contains the
link_login_user_id and link_login_id parameters, which are sent to this
same endpoint to complete the login.If the account has two-factor authentication enabled the totp field is
required. When it’s missing the otp_required error is returned, request the
current six digit code from the user and try again.
Logins are rate limited per IP address, when logging in too often the ip_rate_limit_reached error is returned.
| Param | Type | Required | Description |
|---|---|---|---|
| username | string | true | Username or e-mail address of the account |
| password | string | for password login | Password of the account, leave empty to receive a login link by e-mail |
| totp | string | if 2FA is enabled | Six digit time-based one-time password |
| app_name | string | false | Name of your application, will be shown on the API keys page |
| redirect | string | false | Path the user is sent to after using the e-mailed login link |
| link_login_user_id | UUID | for link login | User ID from the e-mailed login link |
| link_login_id | UUID | for link login | Single-use login ID from the e-mailed login link |
HTTP 201: Created
{
// The API key of the new session
"auth_key": "550e8400-e29b-41d4-a716-446655440000",
"creation_ip_address": "192.0.2.1",
"user_agent": "Mozilla/5.0 ...",
"app_name": "my_app",
"creation_time": "2024-02-04T18:34:13.466276Z",
"last_used_time": "2024-02-04T18:34:13.466276Z",
// The domains this API key can be used on
"valid_domains": ["pixeldrain.com"]
}
When only an e-mail address was entered:
HTTP 202: Accepted
{
"success": true,
"value": "login_link_sent",
"message": "Login link was sent to the user"
}
| Value | HTTP status | Description |
|---|---|---|
| user_not_found | 404 | No account exists with this username or e-mail address |
| password_incorrect | 400 | The entered password is not correct for this account |
| no_login_method_available | 400 | No password was entered and the entered username is not an e-mail address |
| otp_required | 400 | The account has two-factor authentication enabled, send the totp field |
| otp_incorrect | 400 | The entered one-time password is not correct |
| login_link_sent | 202 | Not an error, a login link was e-mailed to the user |
| login_link_already_sent | 400 | The account already has an active login link, wait for it to expire |
| invalid_login_link | 400 | The login link is not valid for this account, it may have expired |
| invalid_email_address | 400 | A login link could not be sent because the e-mail address is invalid |
| request_origin_invalid | 400 | The Origin header of the request does not match the pixeldrain domain names |
| ip_rate_limit_reached | 429 | Too many login attempts from your IP address, try again later |
Returns information about the logged in user account.
HTTP 200: OK
{
"id": "8e08a626-040e-417e-8713-a0f43737a235",
"username": "some_user",
"email": "user@example.com",
"email_verified": true,
// Whether two-factor authentication is enabled
"otp_enabled": false,
// The subscription plan of this account
"subscription": {
"id": "prepaid",
"name": "Prepaid",
"type": "prepaid",
"file_size_limit": 107374182400,
"file_expiry_days": 0,
"storage_space": -1,
"price_per_tb_storage": 4000000,
"price_per_tb_bandwidth": 1000000,
"monthly_transfer_cap": -1,
"file_viewer_branding": true,
"filesystem_access": true,
"filesystem_storage_limit": -1
},
"storage_space_used": 1234567890,
"filesystem_storage_used": 1234567890,
"file_count": 420,
"filesystem_node_count": 1337,
"is_admin": false,
// Prepaid balance in micro euros, 1000000 = €1
"balance_micro_eur": 12500000,
"hotlinking_enabled": true,
// Monthly transfer cap in bytes, 0 means no custom cap is configured
"monthly_transfer_cap": 0,
"monthly_transfer_used": 1234567890,
// File viewer customizations, see PUT /user/file_customization
"file_viewer_branding": null,
// Space separated list of domains which can embed your files
"file_embed_domains": "",
// Whether direct file links skip the file viewer page
"skip_file_viewer": false,
"affiliate_user_name": "",
"checkout_country": "NLD",
"checkout_name": "",
"checkout_provider": ""
}
Updates account settings. Only the fields which are sent with the request are updated.
| Param | Type | Description |
|---|---|---|
| username | string | New username of the account |
| string | New e-mail address. A verification link is sent to the new address, the change only takes effect when the link is used. Sending an empty value removes the e-mail address from the account | |
| password_new | string | New password of the account, between 5 and 50 characters |
| subscription | enum | Activate a prepaid plan, can be ‘prepaid’ or ‘prepaid_lite’. Requires at least €1 of account credit |
| hotlinking_enabled | boolean | Whether other people are allowed to use your account’s data transfer when downloading your files |
| transfer_cap | integer | Custom monthly transfer cap in bytes, cannot exceed the cap of your subscription plan |
| skip_file_viewer | boolean | When enabled, direct links to your files skip the file viewer page |
| embed_domains | string | Space separated list of domains which are allowed to embed your files |
| affiliate_user_name | string | Username of the account which receives affiliate rewards for your purchases, empty to remove |
| checkout_country | string | Three letter country code used for checkout |
| checkout_name | string | Name used for checkout |
| checkout_provider | string | Payment provider used for checkout |
HTTP 200: OK
{
"success": true,
"value": "ok",
"message": "The requested action was successfully performed"
}
When the e-mail address was changed the response is login_link_sent (HTTP 202), see POST /user/login.
| Value | HTTP status | Description |
|---|---|---|
| username_already_registered | 400 | An account with the new username already exists |
| email_address_already_registered | 400 | An account with the new e-mail address already exists |
| invalid_email_address | 400 | The new e-mail address is not valid |
| login_link_already_sent | 400 | The account already has an active verification link, wait for it to expire |
| not_enough_credit_for_prepaid | 400 | Activating a prepaid plan requires at least €1 of account credit |
| invalid_domain_name | 400 | One of the embed domains does not contain a period |
| user_not_found | 404 | The affiliate username does not exist |
Deletes the account. All sessions are logged out immediately and the account is scheduled for deletion in 7 days. If the account logs in again within those 7 days the deletion is cancelled. If the account has an e-mail address configured a message is sent to explain the deletion procedure.
HTTP 200: OK
{
"success": true,
"value": "ok",
"message": "The requested action was successfully performed"
}
Returns a list of all the files on the account, up to a maximum of 50000. The file objects have the same structure as the GET /file/{id}/info response.
| Param | Required | Location | Description |
|---|---|---|---|
| format | false | URL | When set to ‘csv’ the file list is returned as a CSV spreadsheet instead of JSON |
HTTP 200: OK
{
"files": [
{
"id": "1234abcd",
"name": "screenshot.png",
"size": 5694837,
... // Remaining file info fields, see GET /file/{id}/info
}
]
}
Returns all the lists on the account. The list objects have the same structure as the GET /list/{id} response, except that the files array is not included.
| Param | Required | Location | Description |
|---|---|---|---|
| format | false | URL | When set to ‘csv’ the lists are returned as a CSV spreadsheet instead of JSON |
HTTP 200: OK
{
"lists": [
{
"id": "L8bhwx",
"title": "Rust in Peace",
"date_created": "2020-02-04T18:34:13.466276Z",
"file_count": 3,
"can_edit": true
}
]
}
Returns historic usage statistics of the account.
| Param | Required | Location | Description |
|---|---|---|---|
| statistic | true | URL | The statistic to get, can be ‘bandwidth’, ‘transfer_paid’, ‘views’ or ‘downloads’ |
| start | true | URL | Start time of the data, RFC 3339 format |
| end | true | URL | End time of the data, RFC 3339 format |
| interval | true | URL | Duration of each time slot in minutes, between 1 and 1440 |
HTTP 200: OK
{
"timestamps": [
"2024-02-04T18:00:00Z",
"2024-02-04T19:00:00Z"
],
"amounts": [
123456,
654321
]
}
| Value | HTTP status | Description |
|---|---|---|
| not_found | 404 | The statistic in the URL is not one of the allowed values |
| time_parse_failed | 422 | The start or end time was not formatted correctly |
| missing_field | 400 | A required parameter was not sent |
Returns the activity log of the account for one month. The activity log shows files which were removed from the account. The event field can be ‘file_instance_expired’, ‘file_instance_blocked’, ‘file_instance_lost’, ‘filesystem_node_blocked’ or ‘filesystem_node_lost’.
| Param | Required | Location | Description |
|---|---|---|---|
| month | true | URL | The month to get activity for, formatted like ‘2024-02’ |
HTTP 200: OK
[
{
"time": "2024-02-04T18:34:13.466276Z",
"event": "file_instance_expired",
"file_id": "1234abcd",
"file_name": "screenshot.png",
"file_removal_reason": "file_expired"
}
]
| Value | HTTP status | Description |
|---|---|---|
| time_parse_failed | 422 | The month in the URL was not formatted correctly |
Returns the prepaid transaction log of the account for one month. All amounts are in micro euros, 1000000 = €1.
| Param | Required | Location | Description |
|---|---|---|---|
| month | true | URL | The month to get transactions for, formatted like ‘2024-02’ |
HTTP 200: OK
[
{
"time": "2024-02-04T18:34:13.466276Z",
"new_balance": 12500000,
"deposit_amount": 0,
"subscription_charge": -50000,
"storage_charge": -25000,
"storage_used": 1234567890,
"bandwidth_charge": -10000,
"bandwidth_used": 123456789,
"affiliate_amount": 0,
"affiliate_count": 0
}
]
| Value | HTTP status | Description |
|---|---|---|
| time_parse_failed | 422 | The month in the URL was not formatted correctly |
Creates a new invoice to add credit to the account. The response contains a checkout URL where the payment can be completed.
| Param | Type | Required | Description |
|---|---|---|---|
| amount | integer | true | Amount to deposit in micro euros, between 10000000 (€10) and 1000000000000 (€1000000) |
| network | enum | true | Payment method, can be ‘mollie’, ‘paypal’, ‘bancontact’, ‘eps’, ‘ideal’, ‘p24’, ‘trustly’, ‘btc’, ‘btc_lightning’, ‘doge’, ‘xmr’ or ‘ltc’ |
| country | string | true | Two or three letter country code, used for calculating VAT |
| name | string | for ‘ideal’ and ‘p24’ | Full name of the customer, required by some payment methods |
HTTP 200: OK
{
"invoice_id": "abc123",
"checkout_url": "https://payment.provider/checkout/abc123"
}
Returns all the invoices of the account. Amounts are in micro euros, 1000000 = €1.
HTTP 200: OK
[
{
"id": "abc123",
"time": "2024-02-04T18:34:13.466276Z",
"amount": 10000000,
"vat": 2100000,
"country": "NL",
"payment_gateway": "mollie",
"payment_method": "ideal",
"status": "paid"
}
]
Redirects the browser to the checkout page of an open invoice. If the payment does not exist anymore the invoice is cancelled and the browser is redirected back to the website.
| Param | Required | Location | Description |
|---|---|---|---|
| id | true | URL | ID of the invoice to pay |
HTTP 302: Found, with a Location header pointing to the checkout page.
| Value | HTTP status | Description |
|---|---|---|
| not_found | 404 | No invoice exists with this ID |
Configures the appearance of the file viewer page for all the files on the account. This feature requires a subscription plan with file viewer branding support. The image fields accept the ID of an image file on your own account. The image needs to be of type JPEG, PNG, GIF or WebP and can be at most 10 MB large. Fields which are sent with an empty value are removed.
| Param | Type | Description |
|---|---|---|
| theme | string | Color theme of the file viewer |
| header_image | string | ID of an image file to show in the header |
| header_link | string | URL to open when the header image is clicked, must start with https:// |
| background_image | string | ID of an image file to use as page background |
| footer_image | string | ID of an image file to show in the footer |
| footer_link | string | URL to open when the footer image is clicked, must start with https:// |
| affiliate_prompt | string | Affiliate message to show on the download page |
| disable_download_button | boolean | Hides the download button on the file viewer |
| disable_share_button | boolean | Hides the share button on the file viewer |
HTTP 200: OK
{
"success": true,
"value": "ok",
"message": "The requested action was successfully performed"
}
| Value | HTTP status | Description |
|---|---|---|
| branding_image_not_found | 404 | One of the image file IDs does not exist |
| not_found | 404 | One of the image files does not belong to your account |
| invalid_file_type | 400 | One of the image files is not a JPEG, PNG, GIF or WebP image |
| file_too_large | 413 | One of the image files is larger than 10 MB |
| invalid_link | 400 | The header or footer link does not start with https:// |
Creates a new API key for the account. This is the same kind of key that POST /user/login returns, but this endpoint can be used to create extra keys for different applications without logging in again.
| Param | Type | Required | Description |
|---|---|---|---|
| app_name | string | false | Name of your application, will be shown on the API keys page |
HTTP 201: Created
{
"auth_key": "550e8400-e29b-41d4-a716-446655440000",
"creation_ip_address": "192.0.2.1",
"user_agent": "Mozilla/5.0 ...",
"app_name": "my_app",
"creation_time": "2024-02-04T18:34:13.466276Z",
"last_used_time": "2024-02-04T18:34:13.466276Z",
"valid_domains": ["pixeldrain.com"]
}
Returns all the active sessions of the account. Use this to check for sessions which should not be there.
HTTP 200: OK
[
{
"auth_key": "550e8400-e29b-41d4-a716-446655440000",
"creation_ip_address": "192.0.2.1",
"user_agent": "Mozilla/5.0 ...",
"app_name": "",
"creation_time": "2024-02-04T18:34:13.466276Z",
"last_used_time": "2024-02-04T18:34:13.466276Z"
}
]
Invalidates the API key which was used to make this request. This is the logout endpoint.
HTTP 200: OK
{
"success": true,
"value": "ok",
"message": "The requested action was successfully performed"
}
Saves an API key as a login cookie in the browser. This is used by the website to transfer a login session from one pixeldrain domain to another. This endpoint does not use the regular API key authentication, instead the key is passed as a URL parameter. As a security measure this only works from the same IP address which created the API key.
| Param | Required | Location | Description |
|---|---|---|---|
| api_key | true | URL | The API key to save in the pd_auth_key cookie |
HTTP 200: OK, with a Set-Cookie header containing the API key.
{
"success": true,
"value": "ok",
"message": "The requested action was successfully performed"
}
| Value | HTTP status | Description |
|---|---|---|
| authentication_failed | 401 | The API key does not exist |
| invalid_key_ip | 401 | The request came from a different IP address than the one which created the API key |
Manages two-factor authentication on the account. The form parameter action
selects the operation to perform.
Generates a new TOTP secret. The secret is not saved to the account yet, use
the verify action to enable it. The uri field can be rendered as a QR code
for authenticator apps.
HTTP 200: OK
{
"secret": "ABCDEFGHIJKLMNOP23456789ABCDEF23",
"uri": "otpauth://totp/Pixeldrain:some_user?algorithm=SHA1&digits=6&issuer=Pixeldrain&period=30&secret=ABCDEFGHIJKLMNOP23456789ABCDEF23"
}
Verifies a one-time password against the generated secret and enables two-factor authentication on the account.
| Param | Type | Required | Description |
|---|---|---|---|
| action | enum | true | ‘verify’ |
| secret | string | true | The secret from the generate action |
| otp | string | true | The current six digit code from your authenticator app |
Disables two-factor authentication on the account.
HTTP 200: OK
{
"success": true,
"value": "ok",
"message": "The requested action was successfully performed"
}
| Value | HTTP status | Description |
|---|---|---|
| otp_incorrect | 400 | The entered one-time password does not match the secret |
| enum_parse_failed | 422 | The action is not ‘generate’, ‘verify’ or ‘delete’ |