File Upload to S3

Time Status User Agent
Make a request to see history.

URL Expired

The URL for this request expired after 30 days.

This is part 1 of uploading files to an applicant. In this documentation, you'll be uploading to your account's S3.

Secure data access is required

If your call returns with Secure data access is required, you'll need to contact support@fountain.com to enable your account to access secure data.

Part 2 is to link the file to the applicant. See next article on Link Applicant to Files in S3 to complete the upload to the applicant.

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#

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",
  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; }
  public string S3Key { get; init; }
  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; }
}

Curl Example

curl --request POST \
     --url https://api.fountain.com/v2/applicants/id/secure_documents/upload \
     --header 'accept: application/json' \
     --header 'content-type: application/json'