NAV Navbar

Welcome to WeTransfer's public API!

You can use our API to share files, links and love all over the world.

Our APIs

Depending on what you want to make we have two main flavors: transfers and boards.

Transfer API

Built to get files from one place to the other, this is the classic WeTransfer experience. Send up to 2GB of files per transfer and we will handle it with ease, with a built-in 7 day expiry. Once a transfer has been finalized, that's it - it can't be changed.

Board API

Our Board API is similar to what you'll experience in our mobile app. It can store traditional files as well as links, and is flexible in how it displays your items. Additionally - boards do not have an expiry time as long as they receive activity, so you can add items as you see fit. If untouched, they expire after 3 months.

Embed

WeTransfer Embed allows a way to receive files in a form on your website. Embed is not an API of its own, it is an integration using the (transfer) API.

Embed example image

WeTransfer Embed enables you to receive big files

Or: Why you need Embed

Web forms are those things where your users fill in fields, like a form for their name and address when they buy goods. These forms can handle files, but they aren't very good at it. There are two main problems:

  1. Receiving multiple files is hard to do well.
  2. Receiving large (gigantic) files can be problematic. And by large, we mean anything bigger than a picture from a phone camera.

Embed utilizes the power of WeTransfer to solve these problems.

In order to make use of WeTransfer Embed, there are two prerequisites

Prerequisites

Create an account. From there, the creation of Embed will be straightforward.

Go to developers.wetransfer.com and click on Sign in. Create an account by using GitHub or Google. Once completed, this should present you with a link to the dashboard.

Using the dashboard you can create an Embed key, or an Apps key. To make sure you use Embed, click on Generate new code snippet, just below the WeTransfer Embed heading.

Fill out the form, and press "Create".

Now you are presented your embed snippet. Note that in the example below, the embed key is invalid. Please use this code as a reference only.

<div data-widget-host="habitat" id="wt_embed">
  <script type="text/props">
    {
      "wtEmbedKey": "0a2b5190-8914-442d-a877-cbd68d571101",
      "wtEmbedOutput": ".wt_embed_output"
    }
  </script>
</div>
<script async src="https://prod-embed-cdn.wetransfer.net/v1/latest.js"></script>
<!--
  The next input element will hold the transfer link. For testing purposes, you
  could change the type attribute to "text", instead of "hidden".
-->
<input type="hidden" name="wt_embed_output" class="wt_embed_output" />

Adding Embed to your form

That snippet is a piece of code you can place in your form. Open the html page that has your form in your favorite editor.

Somewhere in between your form open and close tags (<form> and </form>), paste this snippet.

With that setup, whenever a user fills out the form; a link to the uploaded material will be submitted in a field with the name wt_embed_output.

In depth

Let's go over that code part by part, to see what it actually does.

<div data-widget-host="habitat" id="wt_embed">

This line creates an element that will display the file selector.


<script type="text/props">
  {
    "wtEmbedKey": "0a2b5190-8914-442d-a877-cbd68d571101",
    "wtEmbedOutput": ".wt_embed_output"
  }
</script>

This chunk of code is the configuration of WeTransfer Embed.


</div>

This closes the element created on the first line. Nothing important going on otherwise.


<script async src="https://prod-embed-cdn.wetransfer.net/v1/latest.js"></script>

This loads the script that has the code to make Embed do its magic.


<!--
  The next input element will hold the transfer link. For testing purposes, you
  could change the type attribute to "text", instead of "hidden".
-->

This is just a bit of inline documentation, for the developers that don't care to read these docs. Feel free to remove these lines from your code base. Or follow the advise and make the input field visible, by changing it from "hidden" to "text".


<input type="hidden" name="wt_embed_output" class="wt_embed_output" />

This is the input field for your form that holds the link to your transfer. It has a name attribute of "wt_embed_output", meaning that it will be sent to your backend system, using that name. The class attribute is used (in this example) to connect embed to it.

Examples

On github.com/WeTransfer/EmbedExamples, we've collected some examples to help you get up to speed.

There are examples for Netlify and Ruby on Rails*. Go over there to see either the end results, or the changes that focus just on adding embed to an existing form.

* = With more to come if you add your examples!

Find us

Feel free to mail your questions, remarks, feedback or praises regarding Embed on developers@wetransfer.com.

SDKs

V2 and V1 Compatible SDKs

V1 compatible SDKs

These SDKs are only compatible with V1 of the API. As they are entirely community-created, feel free to lend a hand and help them become compatible with V2 of the WeTransfer API as well!

Authorization

API keys - where and how

To use our APIs you must provide your API key with every request. Create an API key on our developers portal - currently we require you to have a GitHub or Google account to login. Once you've done so make sure you're on the dashboard page, and select "Create new application" to start generating an API key.

Once you have a key (or multiple keys), please make sure that you keep them in a secret place, and do not share them. If you need to delete and recreate your key (for whatever reason) click on the key in your dashboard, and select "Delete" under actions. NOTE: This will destroy your currently existing key, so you may want to create a new application / key and add the new key to any running systems before deleting the old one.

When you or a user starts your app / script / etc, it/they will need to authorize using the endpoint below.

Key Limits

Any API key is limited to 1000 requests per 24 hours. All requests to the public API count towards this limit.

POST /authorize

Besides the API Key and the Content-Type header, a JSON Web Token (JWT) must be included on all requests other than the authorize request. You may want to submit an authorization request per-user of your application, containing a unique user identifier. We recommend making these user identifiers random and non-sequential, so long as they mean something to your application or internal systems.

These JWTs can be used to interact with the API, and will identify the user to our backend systems. Do not allow (unless your application depends on this functionality) different users to share a unique_identifier, as this will mean that user Alice can access user Bob's transfers. If you do not include the identifier, anyone using your application can potentially access any other transfers and boards created by your application.

Where the API key can be seen as the key to the front door of a house, the user_identifier can be used to create an isolated room within that house.

If you don't use a user identifier when authenticating, you can interact with all boards and transfers you create for your API key. On the other hand, if Alice and Bob are two users using the same API key, but using different user_identifiers, they can interact with their own boards and transfers only, not with the other boards and transfers created with the same API key, for other users.

To retrieve a JWT, send a request including your API key to the following endpoint:

curl -i -X POST "https://dev.wetransfer.com/v2/authorize" \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR PRIVATE API KEY GOES HERE" \
  -d '{"user_identifier":"5eb6b98e-ddaa-4f5b-9d03-7bd4d91aa05f"}'
const createWTClient = require('@wetransfer/js-sdk');

// Please keep in mind that authorization is performed when the client is initialized.
const wtClient = await createWTClient('/* YOUR PRIVATE API KEY GOES HERE */');

// When using the SDK, there is no need to call authorize manually.
// The method is available though, in case you need to access the JWT.
const auth = await wtClient.authorize();
# Using the WeTransfer Ruby SDK...
require 'we_transfer_client'

# Create a WeTransfer client that authorizes requests on your api_key
client = WeTransfer::Client.new(api_key: 'YOUR PRIVATE API KEY GOES HERE')

# Or, by hand.
# If you aren't using the WeTransfer gem, you could POST yourself:
require 'faraday'

faraday = Faraday.new('https://dev.wetransfer.com')
response = faraday.post(
  '/v2/authorize',
  '{}',
  {
    'Content-Type' => 'application/json',
    'x-api-key' => 'YOUR PRIVATE API KEY GOES HERE',
  }
)
// The client is configured with your private API key
// The SDK performs the authorize request once, right before the actual transfer or board creation request
WeTransfer.configure(with: WeTransfer.Configuration(apiKey: "YOUR PRIVATE API KEY GOES HERE"))

Headers

name type required description
x-api-key String Yes Private API key
Content-Type String Yes Must be application/json

Body

name type required description
user_identifier String No A unique (per user of your application) identifier

Response

200 (OK)

If you successfully authenticate, this endpoint will return an HTTP response with a status code of 200 and a body as below.

{
  "success": true,
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqdXN0IGEgc2FtcGxlIHRva2VuLCB0aGUgYWN0dWFsIG9uZSB3aWxsIGhhdmUgZGlmZmVyZW50IGNvbnRlbnQiLCJuYW1lIjoiQW5nZWxhIEJlbm5ldHQiLCJpYXQiOjE1MTYyMzkwMjJ9.fd14EeU1vbj40WtHIYaDwpCOE972DKnrrP8mffioEdg"
}

The token returned here must be sent with subsequent requests. It is not recommended to share this token across clients - if your app is installed on a new device it should at the very least re-authorize on startup. The (optional) user_identifier attribute is very well suited to create different tokens for different clients, using the same API key.

403 (Forbidden)

If you send an API key that is invalid, or you don't send an API key at all, this endpoint will return an HTTP response with a status code of 403 and a body as below.

{
  "success": false,
  "message": "Forbidden: invalid API Key. See https://developers.wetransfer.com/documentation"
}

File names

Both the transfer API and the board API have the ability to work with files. No matter which API you're using, there are constraints that apply to file names.

Removal of characters

Some characters in file names don't interact well with our systems or in some cases with the systems of the receiver. Therefore we will remove them from the file name. This does not alter the content of the file, only the name of the file.

Emojis

Emojis will be removed from a file name. That means that a file with the name the-weather-is-☀️.jpg will be converted into the-weather-is-.jpg.

Special file system characters

We remove characters that have a special meaning to file systems. Think of the /, \ (forward and backward slash), and the : (colon) character, to name a few. A file that has these characters in its name could potentially break a file system. That means that a file with the name the-weather-is-:\.jpg will be converted into the-weather-is-.jpg.

You can use your preferred file names, but the API strips the kind of characters listed above. That means that the upload of an affected file will succeed, but it will end up with a different name than you might expect when it reaches its destination.

File names should have characters

A file with an empty name is not valid. Since the API removes problematic characters, you might hit this issue for files that have a name that consists solely of emojis and special characters. No judgement on your file naming scheme, but after we strip all those characters, we expect there to be something left. If the resulting file name is empty, the API will return an error. See below for an example

If you create a transfer for a file with an empty name, the API responds with an HTTP status code 400 Bad Request, and this body:

{
  "success": false,
  "message": "\"transfer.files.name\" is not allowed to be empty"
}

If you create a board for a file with an empty name, the API responds with an HTTP status code 400 Bad Request, and this body:

{
  "success": false,
  "message": "\"board.files.name\" is not allowed to be empty"
}

Unique names

Your file names should be unique within your board or transfer. If you want to store two files with the same name, we will return an error. In the API we consider file names to be case sensitive. That means that the files polish.txt and Polish.txt are treated as two different files.

If you create a transfer with duplicate file names, the API will return an HTTP status code 400 Bad Request, and this body:

{
  "success": false,
  "message": "\"transfer.files\" position 1 contains a duplicate value"
}

If you create a board with duplicate file names, the API will return an HTTP status code 400 Bad Request, and this body:

{
  "success": false,
  "message": "\"board.files\" position 1 contains a duplicate value"
}

Transfer API

The Transfer API is classic WeTransfer. You might know it well (or you're about to). Use it to upload files, get the link and share magic. We've been using this behind the scenes for ages (an internal version of it powers our macOS app and our Command Line Client and now we're opening it up to you and all your users.

Transfers created through the APIs stick around for 7 days and then vanish forever. They also have a 2GB limit. For now the Transfer API is not connected to Plus accounts, so you'll need to store the transfer link somewhere - just like a web link transfer, there's no way to get the link back if it gets misplaced.

A transfer request consists of the endpoint itself, the headers, and the body, which can contain a message and must contain a list of file objects. The files themselves need a name and a size in bytes - you'll need to compute the size yourself. Most languages provide a way to do this.

Transfers must be created with files. Once the transfer has been created and finalized, the transfer is locked and cannot be further modified.

Create a new transfer

When you create a transfer, you must include at least one file in the transfer create request.

Creating a transfer is to inform the API that you want to create a transfer (with at least one file), but you aren't sending the files already.

What you are sending is the message, a property that describes what this transfer is about, and a collection of file names and their size. This allows the API to set up a place where your files can be uploaded to in a later state. Make sure the size of the file is accurate. Please don't lie to us; we will not be able to handle your files in a later stage...

curl -i -X POST "https://dev.wetransfer.com/v2/transfers" \
  -H "Content-Type: application/json" \
  -H "x-api-key: REPLACE_WITH_YOUR_API_KEY" \
  -H "Authorization: Bearer REPLACE_WITH_YOUR_TOKEN" \
  -d '
    {
      "message":"My very first transfer!",
      "files":[
        {
          "name":"big-bobis.jpg",
          "size":195906
        },
        {
          "name":"kitty.jpg",
          "size":369785
        }
      ]
    }'
const transfer = await wtClient.transfer.create({
  message: 'My very first transfer!',
  files: [
    {
      name: 'big-bobis.jpg',
      size: 195906
    },
    {
      name: 'kitty.jpg',
      size: 369785
    }
  ]
});
# In the current Ruby SDK (version 0.9.x), you can only create a transfer
# and upload the files in one go. This behavior will be split in the upcoming
# major (version 1.0) release.

client = WeTransfer::Client.new(api_key: wetransfer_api_key)

client.create_transfer_and_upload_files(message: 'My very first transfer!') do |builder|
  # Add as many files as you need, using `add_file`, or `add_file_at`
  builder.add_file(name: 'big-bobis.jpg', io: File.open('/path/to/cat_image.jpg', 'rb'))
  builder.add_file_at(path: '/path/to/kitty.jpg')
end

# Access the transfer in your browser:
puts "The transfer can be viewed on #{transfer.url}"
let files: [URL] = [..] // URLs pointing to local files
WeTransfer.createTransfer(saying: "My very first transfer!", fileURLs: files) { result in
    switch result {
    case .success(let transfer):
        // Transfer created
    case .failure(let error):
        // Error creating transfer
    }
}

POST /transfers

Headers

name type required description
x-api-key String Yes Private API key
Authorization String Yes Bearer JWT authorization token
Content-Type String Yes Must be application/json

Parameters

name type required description
message String No Something about cats or coffee, most probably. Defaults to an empty string
files Array Yes A list of file objects to be added to the transfer

File object

name type required description
name String Yes The name of the file you want to show on items list
size Number Yes File size in bytes. Must be accurate. No fooling. Don't let us down!

Response

201 (Created)

After a successful request where a transfer has been created, this endpoint will return an HTTP response with a status code of 201 and a body as below.

{
  "success" : true,
  "id" : "32a6ef6003f1429be0cf1674dd8fbdef20181019143517",
  "message" : "My very first transfer!",
  "state" : "uploading",
  "url" : null,
  "expires_at": "2018-01-01T00:00:00Z",
  "files" : [
    {
      "multipart" : {
        "part_numbers" : 1,
        "chunk_size" : 195906
      },
      "size" : 195906,
      "type" : "file",
      "name" : "big-bobis.jpg",
      "id" : "c964caf6c54343f3b6e9610cb4ac5ea220181019143517"
    },
    {
      "multipart" : {
        "part_numbers" : 1,
        "chunk_size" : 369785
      },
      "size" : 369785,
      "type" : "file",
      "id" : "e7f74773661f2be2bec90e6322864abd20181019143517",
      "name" : "kitty.jpg"
    }
  ]
}
400 (Bad Request)

If the body in the request to this endpoint is not valid json, this endpoint will return an HTTP response with a status code of 400 and a body as below.

{
  "success": false,
  "message": "Unexpected token ! in JSON at position 0. See https://developers.wetransfer.com/documentation"
}

It will list the actual error in the JSON, the above result is just a response to a json string that starts with an exclamation point.

If you set the message for your transfer to something other than a string, this endpoint will return an HTTP response with a status code of 400 and a body as below.

{
  "success": false,
  "message": "\"transfer.message\" must be a string. See https://developers.wetransfer.com/documentation"
}

If you forget to send the files in the JSON of your request, this endpoint will return an HTTP response with a status code of 400 and a body as below.

{
  "success": false,
  "message": "\"transfer.files\" must contain at least 1 items. See https://developers.wetransfer.com/documentation"
}

Request upload URLs

If you look at that response above, you see some pointers that are needed now. All files should be uploaded to a specific place. You just have to get the place from our API. To do that, you ask the API based on the transfer.id, each and every file.id, and for each part (based on the file.multipart.part_numbers property).

To be able to upload a file, it must be split into parts and then each part will be uploaded to pre-signed AWS S3 URLs.

This endpoint can be used to get pre-signed upload URLS for each of a file's parts. These upload URLs are essentially limited access to a storage bucket hosted with Amazon. NOTE: They are valid for an hour and must be re-requested if they expire.

curl -i -X GET "https://dev.wetransfer.com/v2/transfers/{transfer_id}/files/{file_id}/upload-url/{part_number}" \
  -H "x-api-key: your_api_key" \
  -H "Authorization: Bearer jwt_token"
const file = transfer.files[0];

for (
  let partNumber = 0;
  partNumber < file.multipart.part_numbers;
  partNumber++
) {
  // Get the upload url for the chunk we want to upload
  const uploadURL = await wtClient.transfer.getFileUploadURL(
    transfer.id,
    file.id,
    partNumber + 1
  );

  console.log(uploadURL.url)
}
# This functionality is currently not enabled in the SDK.
// This step is not necessary as the request is performed by the SDK right before uploading each file

GET /transfers/{transfer_id}/files/{file_id}/upload-url/{part_number}

Transfer chunks must be 5 megabytes (or more specifically: 5242880 bytes) in size, except for the very last chunk, which can be smaller. Sending too much or too little data will result in a 400 Bad Request error when you finalize the file.

Headers

name type required description
x-api-key String Yes Private API key
Authorization String Yes Bearer JWT authorization token
Content-Type String Yes Must be application/json

Parameters

name type required description
transfer_id String Yes The public ID of the transfer
file_id String Yes The public ID of the file to upload, returned the transfer was created
part_number Number Yes Which part number of the file you want to upload. It will be limited to the maximum multipart.part_numbers response

Response

200 (OK)
{
  "success": true,
  "url": "https://presigned-s3-put-url"
}

The Response Body contains the pre-signed S3 upload url.

404 (Not found)

If you try to request an upload URL for a file that is not in the transfers,, this endpoint will return an HTTP response with a status code of 404 and a body as below.

{
  "success" : false,
  "message" : "Invalid transfer or file id. See https://developers.wetransfer.com/documentation"
}
417 (Expectation Failed)

The API starts counting chunks from number 1, not 0. If you request to upload part 0, this endpoint will return an HTTP response with a status code of 417 and a body as below.

{
  "success" : false,
  "message" : "Chunk numbers are 1-based. See https://developers.wetransfer.com/documentation"
}

File Upload

PUT {signed_url}

Time to actually upload (chunks of) your file. With the pre-signed-upload-url you retrieved in the previous step, you can start uploading!

You will interact directly with Amazon S3. As such, we have no control over the messages sent by S3.

Important: errors returned from S3 will be sent as XML, not JSON. If your response parser is expecting a JSON response it may throw an error here. Please see AWS' S3 documentation for more details about specific responses.

curl -i -T "./path/to/big-bobis.jpg" "https://signed-s3-upload-url"
const axios = require('axios');
const fs = require('fs');

const file = transfer.files[0];
const fileContent = fs.readFileSync('/path/to/file');

for (
  let partNumber = 0;
  partNumber < file.multipart.part_numbers;
  partNumber++
) {
  const chunkStart = partNumber * file.multipart.chunk_size;
  const chunkEnd = (partNumber + 1) * file.multipart.chunk_size;

  // Get the upload url for the chunk we want to upload
  const uploadURL = await wtClient.transfer.getFileUploadURL(
    transfer.id,
    file.id,
    partNumber + 1
  );

  // Use whichever JavaScript library you prefer to upload the chunk:
  // axios, request, fetch, XMLHttpRequest, etc.
  axios({
    url: uploadURL.url,
    method: 'put',
    data: fileContent.slice(chunkStart, chunkEnd),
  });
}
# This functionality is currently not enabled in the SDK.
// To immediately create a transfer and upload its files
let fileURLs: [URL] = [..] // URLs pointing to local files
WeTransfer.uploadTransfer(saying: 'My very first transfer!', containing: fileURLS) { state in
    switch state {
    case .created(let transfer):
        // Transfer created on the server
    case .uploading(let progress):
        // Use the progress object to track the total file upload progress
    case .completed:
        // Transfer is complete
    case .failed(let error):
        // Creating transfer or uploading files failed
    }
}

// Or use the transfer object from the `createTransfer` call
WeTransfer.upload(transfer) { state in
    switch state {
    case .uploading(let progress):
        // Use the progress object to track the total file upload progress
    case .completed:
        // Transfer is complete
    case .failed(let error):
        // Uploading transfer failed
    default:
        break
    }
}

Complete a file upload

In the previous step, you've uploaded your file (potentially in parts) directly to S3. The WeTransfer API has no idea when that is complete. This call informs your transfer object that all the uploading for your file is done.

Again, to inform the API about this event, you need both the transfer id and the file id. Not only that, you currently also have to inform this endpoint on the amount of part numbers.

curl -i -X PUT "https://dev.wetransfer.com/v2/transfers/{transfer_id}/files/{file_id}/upload-complete" \
  -H "Content-Type: application/json" \
  -H "x-api-key: your_api_key" \
  -H "Authorization: Bearer jwt_token" \
  -d '{"part_numbers":1}'
await wtClient.transfer.completeFileUpload(transfer, file);
# This functionality is currently not enabled in the SDK.
// This step is not necessary as the request is performed by the SDK right after all chunks have been uploaded.

PUT /transfers/{transfer_id}/files/{file_id}/upload-complete

Headers

name type required description
x-api-key String Yes Private API key
Authorization String Yes Bearer JWT authorization token
Content-Type String Yes Must be application/json

Parameters

name type required description
transfer_id String Yes The public ID of the transfer
file_id String Yes The public ID of the file to upload, returned the transfer was created
part_numbers String Yes Total number of parts uploaded. It must be the same as multipart.part_numbers response

Response

200 (OK)
{
  "id": "random-hash",
  "retries": 0,
  "name": "big-bobis.jpg",
  "size": 195906,
  "chunk_size": 5242880
}
417

If you try to finalize a file, but didn't actually upload all chunks, this endpoint will return an HTTP response with a status code of 417 and a body as below.

{
  "success": false,
  "message": "Chunks 1 are still missing. See https://developers.wetransfer.com/documentation"
}

Finalize a transfer

After all files are uploaded and finalized, you can close the transfer for modification, and make it available for download.

You do that by calling this endpoint. It informs the API that everything has been completely uploaded.

curl -i -X PUT "https://dev.wetransfer.com/v2/transfers/{transfer_id}/finalize" \
  -H "Content-Type: application/json" \
  -H "x-api-key: your_api_key" \
  -H "Authorization: Bearer jwt_token"
// Finalize transfer
const finalTransfer = await wtClient.transfer.finalize(transfer);

console.log(finalTransfer.url);
# This functionality is currently not enabled in the SDK.
// This step is not necessary as the request is performed by the SDK right after all files have been successfully uploaded.

PUT /transfers/{transfer_id}/finalize

Headers

name type required description
x-api-key String Yes Private API key
Authorization String Yes Bearer JWT authorization token
Content-Type String Yes must be application/json

Parameters

name type required description
transfer_id String Yes The public ID of the transfer where you added the files

Response

200 (OK)

If all went well, the API sends you a lot of data. One of them being the url. That is the thing you will use to access the transfer in a browser.

{
  "id": "random-hash",
  "message": "My first transfer!",
  "state": "processing",
  "url": "https://we.tl/t-12344657",
  "expires_at": "2018-01-01T00:00:00Z",
  "files": [
    {
      "id": "random-hash",
      "name": "big-bobis.jpg",
      "size": 195906,
      "multipart": {
        "part_numbers": 1,
        "chunk_size": 195906
      }
    }
  ]
}

Retrieve transfer information

Once you're done, you might want to know about your transfer and all of its files. You can use your transfer_id and this endpoint to retrieve that information.

GET /transfers/{transfer_id}

curl -iX GET "https://dev.wetransfer.com/v2/transfers/{transfer_id}" \
  -H "x-api-key: your_api_key" \
  -H "Authorization: Bearer jwt_token"

Headers

name type required description
x-api-key String Yes Private API key
Authorization String Yes Bearer JWT authorization token

Parameters

name type required description
transfer_id String Yes The ID of the transfer you've finalized

Response

200 (OK)
{
  "id": "random-hash",
  "message": "My very first transfer!",
  "state": "downloadable",
  "url": "https://we.tl/t-ABcdEFgHi12",
  "expires_at": "2018-01-01T00:00:00Z",
  "files": [
    {
      "id": "another-random-hash",
      "type": "file",
      "name": "big-bobis.jpg",
      "multipart": {
        "chunk_size": 195906,
        "part_numbers": 1
      },
      "size": 195906
    }
  ]
}
404 (Not Found)

When you try to get information from a transfer we cannot find, or that you don't have access to, this endpoint will return an HTTP response with a status code of 400 and a body as below.

{
  "success" : false,
  "message": "Couldn't find Transfer. See https://developers.wetransfer.com/documentation"
}

Board API

The Board API is the latest addition to our Public API. It was originally built with our iOS and , Android apps in mind, but it's also suitable for web/desktop users. It is designed for collecting content rather than transmitting content from A to B (though it can do that, too). It supports both files and links. Boards can be changed - if you hold on to the board's public ID you are able to add and remove items from a board as long as it is live.

Note that boards are "live" indefinitely, so long as they are being viewed. If a board is not accessed for 90 days it is deleted!

Create a new board

Boards are created without items. One the board has been created, items can be added at any time. If you don't add any items, the API will create an empty board.

curl -i -X POST "https://dev.wetransfer.com/v2/boards" \
  -H "Content-Type: application/json" \
  -H "x-api-key: your_api_key" \
  -H "Authorization: Bearer jwt_token" \
  -d '{"name": "Little kittens"}'
const board = await wtClient.board.create({
  name: 'Little kittens'
});
# In the current Ruby SDK (version 0.9.x), you can only create a board
# and upload the items in one go. This behavior will be split in the upcoming
# major (version 1.0) release.

client = WeTransfer::Client.new(api_key: 'YOUR PRIVATE API KEY GOES HERE')

board = client.create_board_and_upload_items(name: 'Little kittens') do |builder|
  builder.add_file(
    name: 'bobis.jpg',
    io: File.open('/path/to/kitty.jpg', 'rb')
  )
  builder.add_file_at(path: '/path/to/kitty.jpg')
  builder.add_web_url(
    url: 'http://www.wetransfer.com',
    title: 'the title that defaults to the url'
  )
end

# Access the board in your browser:
puts "The board can be viewed on #{board.url}"
// This does not create the board server-side, yet. The request is performed
// when files are added to the board for the first time
// (adding links will be supported in the SDK version 2.1)
let board = Board(name: "Little kittens", description: nil)

POST /boards

Headers

name type required description
x-api-key String Yes Private API key
Authorization String Yes Bearer JWT authorization token
Content-Type String Yes Must be application/json

Parameters

name type required description
name String Yes A name or title for your board
description String No A description of your board, if you want it

Response

200 (OK)

If everything goes fine, this endpoint will return an HTTP response with a status code of 200 and a body as below.

{
  "id": "random-hash",
  "name": "Little kittens",
  "description": null,
  "state": "downloadable",
  "url": "https://we.tl/b-random-hash",
  "items": []
}

Later you'll want to interact with your board. Add files and links to it. In order to do that, make sure to note the value of the id property.

400 (Bad Request)

If you forget to set a name for the board, this endpoint will return an HTTP response with a status code of 400 and a body as below.

{
  "success": false,
  "message": "\"board.name\" is required. See https://developers.wetransfer.com/documentation"
}

If you don't send any request body (that holds a board), this endpoint will return an HTTP response with a status code of 400 and a body as below.

{
  "success": false,
  "message": "\"board\" must be an object. See https://developers.wetransfer.com/documentation"
}

A board can hold files and URLs. Lets have a look at how you can add URLs to your board. For that you need the id of the board you created earlier, unless your SDK will handle that for you.

Once a board has been created you can add links like below:

curl -i -X POST "https://dev.wetransfer.com/v2/boards/{board_id}/links" \
  -H "Content-Type: application/json" \
  -H "x-api-key: your_api_key" \
  -H "Authorization: Bearer jwt_token" \
  -d '[{"url": "https://wetransfer.com/", "title": "WeTransfer"}]'
const linkItems = await wtClient.board.addLinks(board, [{
  url: 'https://wetransfer.com/'
}]);
# This functionality is currently not enabled in the SDK.
// Adding links is currently not supported in the SDK but will be added in the SDK version 2.1

Headers

name type required description
x-api-key String Yes Private API key
Authorization String Yes Bearer JWT authorization token
Content-Type String Yes Must be application/json

Request Body

type required description
Array Yes A list of link objects(see below) to add to an existing board
name type required description
url String Yes The complete URL of the link. Must be less than 2000 characters!
title String No The title of the page, defaults to the url. Must be less than 2980 characters!

Response

200 (OK)

If you successfully add a link and a board, this endpoint will return an HTTP response with a status code of 200 and a body as below.

[
  {
    "id": "random-hash",
    "url": "https://wetransfer.com/",
    "meta": {
      "title": "WeTransfer"
    },
    "type": "link"
  }
]
400 (Bad Request)

If you don't send any request body (that holds the link), this endpoint will return an HTTP response with a status code of 400 and a body as below.

{
  "success": false,
  "message": "\"board.links\" must be an array. See https://developers.wetransfer.com/documentation"
}

If the url of the link is longer that 2000 characters, this endpoint will return an HTTP response with a status code of 400 and a body as below.

{
  "success": false,
  "message": "\"board.links.url\" length must be less than or equal to 2000 characters long. See https://developers.wetransfer.com/documentation"
}

If the title of the link is longer that 2980 characters, this endpoint will return an HTTP response with a status code of 400 and a body as below.

{
  "success": false,
  "message": "\"board.links.title\" length must be less than or equal to 2980 characters long. See https://developers.wetransfer.com/documentation"
}
404 (Not Found)

If you try to add links to a board that we cannot find, or that doesn't belong to you, this endpoint will return an HTTP response with a status code of 404 and a body as below.

{
  "success": false,
  "message":"This board does not exist. See https://developers.wetransfer.com/documentation"
}

If you forget the fill the board_id in the request, this endpoint will return an HTTP response with a status code of 404 and a body as below.

{
  "success": false,
  "message": "use correct endpoint. See https://developers.wetransfer.com/documentation"
}

Add files to a board

Note that files need a name and a size in bytes - you'll need to compute the size yourself. Most languages provide a way to do this.

Once a board has been created you can add files like so:

curl -i -X POST "https://dev.wetransfer.com/v2/boards/{board_id}/files" \
  -H "Content-Type: application/json" \
  -H "x-api-key: your_api_key" \
  -H "Authorization: Bearer jwt_token" \
  -d '[{"name": "big-bobis.jpg", "size": 195906}]'
const fileItems = await wtClient.board.addFiles(board, [{
  name: 'kittie.gif',
  size: 1024
}]);
# This functionality is currently not enabled in the SDK.
let board = Board(name: "Little kittens", discription: nil)

let fileURLs: [URL] = [..] // URLs pointing to local files
let files: [File]
do {
    // Create a File object for each URL.
    // When initialization fails, an error will be thrown
    files = try fileURLs.map({ try File(url: $0) })
} catch {
    // Please handle thrown errors gracefully
}
WeTransfer.add(files, to: board) { result in
    // Handle result success or failure
}

POST /boards/{board_id}/files

Headers

name type required description
x-api-key String Yes Private API key
Authorization String Yes Bearer JWT authorization token
Content-Type String Yes Must be application/json

Parameters

name required description
files Yes A list of file objects to send to an existing board (see below)

File object

name type required description
name String Yes The name of the file you want to show on items list. Must be less than 255 characters.
size Number Yes File size in bytes. Must be accurate. No fooling. Don't let us down!

Response

200 (OK)

If you add one file (or many files) that have a valid name and a size, this endpoint will return an HTTP response with a status code of 200 and a body as below.

[
  {
    "id": "random-hash",
    "name": "big-bobis.jpg",
    "size": 195906,
    "multipart": {
      "id": "some random id",
      "part_numbers": 1,
      "chunk_size": 195906
    },
    "type": "file"
  }
]

After this successful request, this endpoint will return an JSON holding an object for each file you want to add to the board. That object is helpful in the next step when we want to request a place where we can upload our file.

The important parts in the response are the id of the file, the id of the multipart object, together with its part_numbers.

400 (Bad Request)

If the request does not have a properly formatted file list in the body of the request, this endpoint will return an HTTP response with a status code of 400 and a body as below.

{
  "success": false,
  "message": "\"board.files\" must be an array. See https://developers.wetransfer.com/documentation"
}

In case you send a file with a size of 0 (zero), this endpoint will return an HTTP response with a status code of 400 and a body as below.

{
  "success": false,
  "message": "\"board.files.size\" must be greater than 1. See https://developers.wetransfer.com/documentation"
}

In case you send a file with an empty name, this endpoint will return an HTTP response with a status code of 400 and a body as below.

{
  "success": false,
  "message": "\"board.files.name\" is not allowed to be empty. See https://developers.wetransfer.com/documentation"
}

If case you forget to send the file name property key and value altogether, this endpoint will return an HTTP response with a status code of 400 and a body as below.

{
  "success": false,
  "message": "\"board.files.name\" is required. See https://developers.wetransfer.com/documentation"
}

If case you forget to send the file size property key and value altogether, this endpoint will return an HTTP response with a status code of 400 and a body as below.

{
  "success": false,
  "message": "\"board.files.size\" is required. See https://developers.wetransfer.com/documentation"
}

If case you use a file name that is longer than 255 characters, this endpoint will return an HTTP response with a status code of 400 and a body as below.

{
  "success": false,
  "message": "\"board.files.name\" length must be less than or equal to 255 characters long. See https://developers.wetransfer.com/documentation"
}

Request upload URL

Now that you've informed us about the file(s) you want to upload, it is time to request upload URLs. Each chunk of the file has its own upload url.

GET /boards/{board_id}/files/{file_id}/upload-url/{part_number}/{multipart_upload_id}

To be able to upload a file, it must be split into chunks, and uploaded to different pre-signed URLs. This endpoint can be used to get pre-signed upload URLs for each of a file's parts. These upload URLs are essentially limited access to a storage bucket hosted with Amazon. They are valid for an hour and must be re-requested if they expire.

Use the fields from the previous response; now you need the id of the file, the id of the multipart, and you must request a upload-url for all of your part_numbers.

curl -i -X GET "https://dev.wetransfer.com/v2/boards/{board_id}/files/{file_id}/upload-url/{part_number}/{multipart_upload_id}" \
  -H "x-api-key: your_api_key" \
  -H "Authorization: Bearer jwt_token"
const file = fileItems[0];

for (
  let partNumber = 0;
  partNumber < file.multipart.part_numbers;
  partNumber++
) {
  // Get the upload url for the chunk we want to upload
  const uploadURL = await wtClient.board.getFileUploadURL(
    board.id,
    file.id,
    partNumber + 1
  );

  console.log(uploadURL.url)
}
# This functionality is currently not enabled in the SDK.
// This step is not necessary as the request is performed by the SDK right before uploading each file

Headers

name type required description
x-api-key String Yes Private API key
Authorization String Yes Bearer JWT authorization token

Parameters

name type required description
board_id String Yes The public ID of the board where you added the files
file_id String Yes The public ID of the file to upload, returned when adding items
part_number Number Yes Which part number of the file you want to upload. It will be limited to the maximum multipart.part_numbers response
multipart_upload_id Number Yes The upload ID issued by AWS S3, which is available at multipart.id

Response

200 (OK)

If you successfully request an upload-url, this endpoint will return an HTTP response with a status code of 200 and a body as below.

{
  "success": true,
  "url": "https://a-very-long-pre-signed-s3-put-url"
}

Important

Files on a board must have a chunk size of 6 megabytes (or more precisely 6291456 bytes), except for the very last chunk, which can be smaller. Sending too much or too little data will result in a 400 Bad Request error when you finalize the file. As with transfers: Do not let us down.

The response body contains the pre-signed S3 upload url. You will use that in the next step when you upload the contents.

400 (Bad request)

If a request is made for a part, but no multipart_upload_id is provided, this endpoint will return an HTTP response with a status code of 400.

401 (Unauthorized)

If you try to request an upload URL for a file that is not in one of your boards, this endpoint will return an HTTP response with a status code of 401.

404 (Not Found)

If you try to request an upload URL for a file, but you provide an invalid file id, this endpoint will return an HTTP response with a status code of 404.

{
  "success": false,
  "message": "File not found. See https://developers.wetransfer.com/documentation"
}

File Upload

PUT {signed_url}

You're communicating directly with Amazons' S3, not with our API. Please note: errors returned from S3 will be sent as XML, not JSON. If your response parser is expecting a JSON response it may throw an error here. Please see AWS' S3 documentation for more details about specific responses.

curl -i -T "./path/to/big-bobis.jpg" "https://signed-s3-upload-url"
const axios = require('axios');
const fs = require('fs');

const file = fileItems[0];
const fileContent = fs.readFileSync('/path/to/file');

for (
  let partNumber = 0;
  partNumber < file.multipart.part_numbers;
  partNumber++
) {
  const chunkStart = partNumber * file.multipart.chunk_size;
  const chunkEnd = (partNumber + 1) * file.multipart.chunk_size;

  // Get the upload url for the chunk we want to upload
  const uploadURL = await wtClient.board.getFileUploadURL(
    board.id,
    file.id,
    partNumber + 1,
    file.multipart.id
  );

  // Use whichever JavaScript library you prefer to upload the chunk:
  // axios, request, fetch, XMLHttpRequest, etc.
  axios({
    url: uploadURL.url,
    method: 'put',
    data: fileContent.slice(chunkStart, chunkEnd),
  });
}
# This functionality is currently not enabled in the SDK.
// To immediately create a board and upload files
let fileURLs: [URL] = [..] // URLs pointing to local files
WeTransfer.uploadBoard(named: "Little Kittens", description: nil, containing: fileURLs) { state in
    switch state {
      case .created(let board):
          // Board created, public URL available
      case .uploading(let progress)
          // Use the progress object to track the total file upload progress
      case .completed:
          // All files in board have completed uploading
      case .failed(let error):
          // Either creating the board or uploading files has failed
    }
}

// Or use an existing board with files added to it that aren't yet uploaded
WeTransfer.upload(board) { state in
    switch state {
    case .uploading(let progress):
        // Use the progress object to track the total file upload progress
    case .completed:
        // File upload is complete
    case .failed(let error):
        // Uploading files failed
    default:
        break
    }
}

Complete a file upload

After all of the file parts have been uploaded, the file must be marked as complete.

curl -i -X PUT "https://dev.wetransfer.com/v2/boards/{board_id}/files/{file_id}/upload-complete" \
  -H "Content-Type: application/json" \
  -H "x-api-key: your_api_key" \
  -H "Authorization: Bearer jwt_token"
await wtClient.board.completeFileUpload(board, file);
# This functionality is currently not enabled in the SDK.
// This step is not necessary as the request is performed by the SDK right after all chunks have been uploaded.

PUT /boards/{board_id}/files/{file_id}/upload-complete

Headers

name type required description
x-api-key String Yes Private API key
Authorization String Yes Bearer JWT authorization token
Content-Type String Yes must be application/json

Parameters

name type required description
board_id String Yes The public ID of the board where you added the files
file_id String Yes The public ID of the file to upload, returned when adding items

Response

200 (OK)

Assuming you've uploaded all chunks of the file to the signed link(s) provided by the previous request, this endpoint will return an HTTP response with a status code of 200 and a body as below.

{
  "success": true,
  "message": "File is marked as complete."
}
400 (Bad Request)

If you call this endpoint before you've uploaded all chunks of the file to the signed link(s) provided by the previous request, this endpoint will return an HTTP response with a status code of 400 and a body as below.

{
  "success": false,
  "message": "expected at least 1 part. See https://developers.wetransfer.com/documentation"
}
404 (Not Found)

If you call this endpoint but use an invalid file_id in the URL, this endpoint will return an HTTP response with a status code of 404 and a body as below.

{
  "success": false,
  "message": "File not found. See https://developers.wetransfer.com/documentation"
}

Retrieve a board's information

GET /boards/{board_id}

Retrieve information about an existing board.

curl -i -X GET "https://dev.wetransfer.com/v2/boards/{board_id}" \
  -H "x-api-key: your_api_key" \
  -H "Authorization: Bearer jwt_token"
// Retrieving board information is currently not supported in the SDK but will be added in the SDK version 2.1

Headers

name type required description
x-api-key String Yes Private API key
Authorization String Yes Bearer JWT authorization token

Parameters

name type required description
board_id String Yes The public ID of the board where you added the files

Response

If you request a board that is yours, this endpoint will return an HTTP response with a status code of 200 and a body as below.

200 (OK)
{
  "id": "board-id",
  "state": "processing",
  "url": "https://we.tl/b-the-boards-url",
  "name": "Little kittens",
  "description": null,
  "items": [
    {
      "id": "random-hash",
      "name": "kittie.gif",
      "size": 195906,
      "multipart": {
        "part_numbers": 1,
        "chunk_size": 195906
      },
      "type": "file"
    },
    {
      "id": "different-random-hash",
      "url": "https://wetransfer.com",
      "meta": {
        "title": "WeTransfer"
      },
      "type": "link"
    }
  ]
}
403 (Forbidden)

If you try to request info from a board that is not yours, this endpoint will return an HTTP response with a status code of 403 and a body as below.

{
  "success": false,
  "message": "You'\''re not a member of this board (123456). See https://developers.wetransfer.com/documentation"
}

Errors

In working with the WeTransfer API you might come across some errors. Here are some of the most common, and what you can do about them.

Most common errors

401 Unauthorized
{"message":"Unauthorized"}
Explanation:

You've forgotten to send us your API key, or it's being improperly sent.

Solution:

Make sure that you're sending your API key with each request - see the example code, or add a header like so: `x-api-key: your_api_key`.

404 Resource not found
{"message":"Resource not found"}
Explanation

The endpoint you are trying to hit doesn't exist.

Solution

Check that you've spelled the endpoint correctly, and remember that our API is versioned - dev.wetransfer.com/authorize won't work, but dev.wetransfer.com/v2/authorize will. Also make sure that you're using the correct version of the API - dev.wetransfer.com/v1/boards won't work!

415 Unsupported Content-Type header
{"message":"Unsupported Content-Type header. Please set Content-Type to 'application/json'."
Explanation:

You've sent a request to the API without a `Content-Type` set, or incorrectly set.

Solution:

Make sure that you're sending a Content-Type: application/json header with each request.

405 Unsupported HTTP Method
{"message":"The HTTP method or resources may not be supported."}
Explanation:

You've sent a request to an endpoint using the wrong HTTP method. For example, you've sent a POST request to an endpoint that expects a GET.

Solution:

Make sure that you're using the correct HTTP verb for each endpoint.

429 API Rate Limit
{"message":"Limit Exceeded Exception"} or 429 response
Explanation:

You've exceeded your rate limit.

Solution:

Try again but with fewer requests in a given time period, wait until tomorrow, or even better: email us (developers@wetransfer.com) and we can talk extending the limit.

400 Expected X to be Y
{"message":"Expected 1200 to be 3243214"}
Explanation:

If the size of the file you send does not match the size of the file you told us to expect, you'll see this message when you send a /complete request.

Solution:

Check that you're properly computing the size of the file, or that you're uploading all the required chunks (before sending the complete call).

A note about file upload errors

Because file uploads go directly to S3, any errors during this step of the process will be returned in XML. Please see the official S3 documentation for details of individual AWS S3 responses.

Error codes

The WeTransfer API uses the following conventional error codes:

Code Meaning
400 Bad Request -- Your request is invalid.
403 Forbidden -- Your API key is wrong.
404 Not Found -- The specified resource could not be found.
405 Method Not Allowed -- You tried to access a transfer or board using the wrong HTTP verb.
406 Not Acceptable -- You requested a format that isn't json.
410 Gone -- The transfer or board requested has been removed from our servers.
418 I'm a teapot.
429 Too Many Requests -- You're requesting too many things! Slow down!
500 Internal Server Error -- We had a problem with our server. Try again later.
503 Service Unavailable -- We're temporarily offline for maintenance. Please try again later.

Changelog

date Changes made Product affected
2018-10-29 Add Swift code snippets Documentation
2018-10-24 Updated V2 and V1 with character limits for link & file listings APIs / Docs
2018-09-25 Release of V2 of the API with Transfers and Boards! All
2018-07-25 Clarify shortened_url, copy fixes Documentation
2018-07-24 Add PHP code snippets Documentation
2018-07-18 Clarify a couple of required params and add errors documentation Documentation
2018-07-10 Deprecate TLS 1.0 and 1.1 on Developers Portal Portal
2018-07-06 Add changelog to documentation Documentation
2018-06-22 Add ability for users to create multiple keys Portal
2018-05-30 Add HSTS header to API endpoints API and Portal
2018-05-14 Release initial version of API to the world All
Syntax shown
  • Welcome to WeTransfer's public API!
  • Embed
  • SDKs
  • Authorization
  • File names
  • Transfer API
  • Board API
  • Errors
  • Changelog