| Time | Status | User Agent |  |
| :-- | :-- | :-- | :-- |
| Make a request to see history. |

#### URL Expired

The URL for this request expired after 30 days.

This is Part 2 to adding files to an applicant's profile. Please refer to [File Upload to S3](https://developer.fountain.com/reference/upload-document) for Part 1.

> 🚧
>
> ### "Key" Clarification   [Skip link to "Key" Clarification](https://developer.fountain.com/reference/post_v2-applicants-id-secure-documents-link-upload#key-clarification)
>
> Not to be confused by S3's Object 'Key', the `key` refers to Fountain's identifier for the file you're uploading. In the Product, this is known as "Data Key". The S3 Object Key is passed in as `s3_key`.

We recommend using the same `key` for similar files you're collecting from applicants to prevent the creation of duplicate data keys. Example, if all your applicants have a resume, upload all the resume files as `resume_file`.

Python

```python
import json
import xml.etree.ElementTree
import mimetypes
import os
import requests

HOST="api.fountain.com"
API_KEY="K-9NB9DkxU7NdrB9eRBHCA"
APPLICANT="6d0d9434-2817-4b96-9ad4-e33a7f2a0adb"
FILENAME="dog.jpg"
UPLOAD_FILE=(FILENAME, open(FILENAME, 'rb'), mimetypes.guess_type(FILENAME)[0])
UPLOAD_KEY="test"

UPLOAD_URL="https://%s/v2/applicants/%s/secure_documents/upload" % (HOST, APPLICANT)
LINK_UPLOAD_URL="https://%s/v2/applicants/%s/secure_documents/link_upload" % (HOST, APPLICANT)

HEADERS = { 'X-ACCESS-TOKEN': API_KEY }

upload_response = requests.post(UPLOAD_URL, headers=HEADERS)
upload_response_json = json.loads(upload_response.text)
form_data = upload_response_json['form_data']
form_data['Content-type'] = ""

s3_upload_response = requests.post(upload_response_json['url'], data=form_data, files={'file': UPLOAD_FILE})

s3_upload_response_xml = xml.etree.ElementTree.fromstring(s3_upload_response.text)
s3_filename = s3_upload_response_xml.find("Key").text

finish_upload_data = {
    'key': UPLOAD_KEY,
    's3_key': s3_filename,
    'size': os.path.getsize(FILENAME),
}
finish_upload_response = requests.post(LINK_UPLOAD_URL, headers=HEADERS, json=finish_upload_data)
print(finish_upload_response)
```

C#

```csharp
using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using System.Xml.XPath;

var localFilePath = @"/Users/userName/Downloads/the_file.png";
var host = "https://us-2.fountain.com";
var apiKey = "your_api_key";
var applicantId = "29345e1b-6224-467a-964c-da077ebed994";

// Open file stream to the local file on disk
var fileStream = new FileStream(localFilePath, FileMode.Open);

// Create a new HttpClient with default headers for the API key
var hireClient = new HttpClient();
hireClient.DefaultRequestHeaders.Add("X-ACCESS-TOKEN", apiKey);

// Get an upload URL so that the file can be uploaded to the applicant
var urlResponse = await hireClient.PostAsync($"{host}/api/v2/applicants/{applicantId}/secure_documents/upload", null);
var presignedPayload = await urlResponse.Content.ReadFromJsonAsync<Payload>(new JsonSerializerOptions { PropertyNameCaseInsensitive = true, PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower });

// Upload the file to the presigned URL with a multipart form
var formData = new MultipartFormDataContent();

// Copy the form data from the presignedPayload to the formData
foreach (var item in presignedPayload.FormData)
{
  formData.Add(new StringContent(item.Value), item.Key);
}
formData.Add(new StringContent(""), "Content-Type"); // required due to a content validation rule
formData.Add(new StreamContent(fileStream), "file", Path.GetFileName(localFilePath));

// POST the file to the presigned URL (S3)
var s3Client = new HttpClient();
var uploadResponse = await s3Client.PostAsync(presignedPayload.Url, formData);
var textResponse = await uploadResponse.Content.ReadAsStringAsync();
if (uploadResponse.StatusCode != HttpStatusCode.Created)
{
  Console.WriteLine("Error uploading file");
  Console.WriteLine(textResponse);
  return;
}
var xmlDocument = new XPathDocument(new StringReader(textResponse));
var navigator = xmlDocument.CreateNavigator();
var keyNode = navigator.SelectSingleNode("/PostResponse/Key");
if (keyNode is null) {
  Console.WriteLine("Unexpected response from S3");
  Console.WriteLine(textResponse);
  return;
}

// Now link the uploaded file residing on S3 to the applicant
var linkResponse = await hireClient.PostAsJsonAsync($"{host}/api/v2/applicants/{applicantId}/secure_documents/link_upload", new LinkPayload
{
  Key = "some_file", // this is the data key you are uploading the file to
  S3Key = keyNode.Value,
  Size = fileStream.Length.ToString()
}, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower });
if (linkResponse.StatusCode != HttpStatusCode.OK)
{
  Console.WriteLine($"Error linking file: {linkResponse.StatusCode}");
  return;
}

public readonly struct LinkPayload
{
  public string Key { get; init; } // data key
  public string S3Key { get; init; } // s3 key
  public string Size { get; init; }
}

public readonly struct Payload
{
    public string Url { get; init; }
    public string Host { get; init; }
    public Dictionary<string, string> FormData { get; init; }
    public int MinSize { get; init; }
    public int MaxSize { get; init; }
    public bool OnlyImages { get; init; }
    public string Type { get; init; }
}
```

id

string

required

s3_key

string

The key to upload to S3

key

string

Fountains file identifier.

# ``200      OK

# ``401      Unauthorized

ShellNodeRubyPHPPython

```

xxxxxxxxxx

curl --request POST \

--url https://api.fountain.com/v2/applicants/id/secure_documents/link_upload \

--header 'accept: application/json' \

--header 'content-type: application/json'
```

Click `Try It!` to start a request and see the response here! Or choose an example:

application/json

``200``401

Updated3 months ago

* * *

Did this page help you?

Yes

No
