/documents

Upload supporting documentation for KYC

During the KYC onboarding process, it may be necessary to upload supporting documentation. This endpoint will support the upload of images of that documentation.

Requests

Requirements:

  • NOTE: Content-Type is different from all other endpoints. Set to multipart/form-data
  • Classic KYC Flow: use either single or multiple document requests
  • Journeys KYC Flow: use single document request
  • NOTE: Document URL's are not supported. This endpoint requires you send a locally stored document
  • Maximum request size is 20 mb.

📘

Handling the multipart/form-data boundary value

NodeJS
Do not manually set Content-Type, as this will not include a 'boundary.' Let your request package automatically detect the content type so that it can automatically generate a boundary.

Python
multipart/form-data is the default Content-Type for the python requests module; when using that module, don't set the header's Content-Type, but instead let the requests module use its default, and it will properly determine the multipart/form-data boundary value for you.

Authorization / Authentication

Apps using Access Token Authorization

Use a valid access token in a Authorization: Bearer request header.

See Authenticating with an Access Token for more details.

Apps using ECDSA Authentication

Both authsignature and usersignature headers are required for this request. The usersignature header should be generated with a keypair registered to the user (either registered from the /register endpoint or the /register_wallet endpoint).

See the section on ECDSA Authentication for more detail about ECDSA signature generation.

Uploading a single document

Classic KYC Flow

There are two options for structuring your uploads. You can either upload a single document at a time, or use a structure that allows for multiple documents in a single upload.

Single document format:
Driver's license example - requires 2 uploads and 2 separate calls (front and back):

/documents request for front:

},
...
  "mime_type": "image/jpeg",
  "document_type": "id_drivers_license",
  "description": "front of driver's license"
}

/documents request for back:

},
...
  "mime_type": "image/jpeg",
  "document_type": "id_drivers_license",
  "description": "back of driver's license"
}

Journeys KYC Flow

Only a single document will ever be required, so you don't need the call for multiple docs found below.

You will make two calls, one with is_front set to true and one call with it set to false.

Classic Flow Single Document/Journeys Flow Request

🚧

Provide clear pictures

Blurry, dark, or otherwise illegible pictures of documents will not be approved regardless of KYC flow, but Journeys flow users should be extra cautious since documents are reviewed by an automated system. Please see Alloy's image guidelines here.

POST /0.2/documents HTTP/1.1
sandbox.silamoney.com
Content-Type: application/json
// if using OAuth2
Authorization: Bearer [GENERATED JWT TOKEN HERE]
// if using ECDSA
authsignature: [GENERATED AUTHSIGNATURE HEX STRING HERE]
usersignature: [GENERATED USERSIGNATURE HEX STRING HERE]

"files": {
  "file": <file_binary_data>
},
"data": {
  "header": {
    "created": 1234567890, 
    "auth_handle": "handle.silamoney.eth", 
    "user_handle":"user.silamoney.eth", 
    "version": "0.2", 
    "crypto": "ETH", 
    "reference": "<your unique id>"
  }, 
  "message": "header_msg",
  "name": "CA drivers license",
  "filename": "img_201901022_193206",
  "hash": "046a9aaa83711158c3c4afa585a30be3bee8a34231ee72caac625faef48b4abe",
  "mime_type": "image/jpeg",
  "document_type": "id_drivers_license",
  "description": "front of driver's license",
  "verification_uuid": <uuid of verification record>, //required for Journeys, disregard for Classic
  "provider": "ALLOY", //disregard for Classic
  "is_front": true //required for Journeys, disregard for Classic
}
  
***

HTTP/1.1 200 OK

{
  "success": true,
  "status": "SUCCESS",
  "message": "File uploaded successfully.",
  "response_time_ms": "1171",
  "reference_id": "<your unique id>",
  "document_id": "05e313a5-2cb4-4638-bf74-debe96b931ee"
}
const document = {  
  filePath: '/path/to/file',  // In case of you have physical file path and using this SDK on server
  fileObject: 'file object', // In case of you not have physical file path and using this SDK on client
  fileBuffer: 'Uint8Array file buffer',// In case of you not have physical file path and using this SDK on client
  filename: 'file-name',    
  mimeType: 'image/png',    
  documentType: 'doc_green_card',   
  name: 'file name', // Optional    
  description: 'some file description', // Optional 
};
// fileObject and fileBuffer both are required if file send as object 
// OR filePath is required if not send fileObject and fileBuffer

const res = await sila.uploadDocument(userHandle, userPrivateKey, document);    

// Success Response Object  
console.log(res.statusCode); // 200 
console.log(res.data.success); // true  
console.log(res.data.status); // SUCCESS    
console.log(res.data.message); // File uploaded successfully    
console.log(res.data.reference_id); // some-uuid-code   
console.log(res.data.document_id); // other-uuid-code
import hashlib

f = open("/path/to/file", "rb")
file_contents = f.read()
f.close()

payload = {
    "user_handle": user_handle,
    "filename": "file-name",
    "hash": hashlib.sha256(file_contents).hexdigest(),
    "mime_type": "image/png",
    "document_type": "id_drivers_license", # You can obtain this value from the listSupportedDocuments
    "name": "some file name", # Optional
    "description": "some file description" # Optional
}

response = silasdk.Documents.uploadDocument(app, payload, file_contents, user_private_key)

# Success Response Object
{
  "success": true,
  "status": "SUCCESS",
  "message": "File uploaded successfully.",
  "reference_id": "2624a0f6-e913-4e09-bfd7-c1bc10543483",
  "document_id": "05e313a5-2cb4-4638-bf74-debe96b931ee"
}
UploadDocumentMessage message = UploadDocumentMessage.builder()
        .userHandle("user_handle") // The user handle
        .userPrivateKey("user_private_key") // The user's private key
        .filePath("/path/to/file") // Full path to the file
        .filename("logo-geko") // File name (without extension)
        .mimeType("image/png") // File mime-type
        .documentType("doc_green_card") // Document type
        .identityType("other") // Optional. Identity type 
        .name("some file name") // Optional. Descriptive name of the document
        .description("some file description") // Optional. General description of the document
        .build();
ApiResponse response = api.uploadDocument(message);

// Success response
System.out.println(response.getStatusCode()); // 200
DocumentsResponse parsedResponse = (DocumentsResponse) response.getData();
System.out.println(parsedResponse.getSuccess()); // true
System.out.println(parsedResponse.getStatus()); // SUCCESS
System.out.println(parsedResponse.getMessage()); // File uploaded successfully
System.out.println(parsedResponse.getDocumentId());
System.out.println(parsedResponse.getReferenceId());
$userHandle = 'user.silamoney.eth';
$privateKey = 'some private key';
$filePath = '/path/to/file';
$fileName = 'some-image'; // The name of the file (without the extension)
$mimeType = 'image/png'; // The mime type of the file
$documentType = 'doc_green_card'; // One of Supported Document Types. You can get this from getDocumentTypes
$identityType = 'other'; // Matching Identity Type for Document Type. You can get this from getDocumentTypes
$name = 'file name'; // Optional. Descriptive name of the document.
$description = 'some file description'; // Optional. General description of the document.
$response = $client->uploadDocument($userHandle, $privateKey, $filePath, $fileName, $mimeType, $documentType, $identityType, $name, $description);
// or without optional parameteres name and description
$response = $client->uploadDocument($userHandle, $privateKey, $filePath, $fileName, $mimeType, $documentType, $identityType);

echo $response->getStatusCode(); // 200
echo $response->getData()->success; // TRUE
echo $response->getData()->status; // SUCCESS
echo $response->getData()->message; // File uploaded successfully.
echo $response->getData()->reference_id; // The reference uuid
echo $response->getData()->document_id; // The document uuid
//Load your informations
string identityType = null;  // identityType is optional
string name = null;   // Name is optional
string description = null; // Description is optional

var response = api.UploadDocument(userHandle, privateKey, filepath, filename, mimeType, documentType, identityType, name, description);


// Success Object Response
Console.WriteLine(response.StatusCode); // 200  
var parsedResponse = (DocumentResponse)response.Data;
Console.WriteLine(parsedResponse.Success); // true
Console.WriteLine(parsedResponse.Status); // SUCCESS
Console.WriteLine(parsedResponse.Message); // File uploaded successfully
Console.WriteLine(parsedResponse.ReferenceId); // some-uuid-code
Console.WriteLine(parsedResponse.DocumentId); // other-uuid-code
Console.WriteLine(parsedResponse.ResponseTimeMs); // API responses time

Multiple Documents - Classic KYC Flow

Multiple documents can be uploaded in a single request by using a different request format. This format can also be used to upload a single document if desired.

  • files: the files you wish to upload. These must be in binary format, URLs are not supported
  • data: the header information and the file_metadata
  • file_metadata: the document metadata for each file you are uploading

🚧

Labels must match!

The labels you use for the files in the "files" section must match the ones used in the "file_metadata" section.

POST /0.2/documents HTTP/1.1
sandbox.silamoney.com
Content-Type: application/json
// if using OAuth2
Authorization: Bearer [GENERATED JWT TOKEN HERE]
// if using ECDSA
authsignature: [GENERATED AUTHSIGNATURE HEX STRING HERE]
usersignature: [GENERATED USERSIGNATURE HEX STRING HERE]

"files": {
  "my_file_1": <file_1_binary_data>,
  "my_file_2": <file_2_binary_data>,
  "your_file_1": <file_3_binary_data>
}

"data": {
  "header": {
    "created": 1234567890,
    "auth_handle": "handle.silamoney.eth",
    "user_handle":"user.silamoney.eth",
    "version": "0.2",
    "crypto": "ETH",
    "reference": "<your unique id>"
  },
  "file_metadata": {
    "my_file_1": {
      "name": "CA drivers license",
      "filename": "img_201901022_193206",
      "hash": "046a9aaa83711158c3c4afa585a30be3bee8a34231ee72caac625faef48b4abe",
      "mime_type": "image/jpeg",
      "document_type": "id_drivers_license",
      "description": "front of driver's license"
    },
    "my_file_2": {
      "name": "CA drivers license",
      "filename": "img_201901022_193207",
      "hash": "146a9aaa83711158c3c4afa585a30be3bee8a34231ee72caac625faef48b4abf",
      "mime_type": "image/jpeg",
      "document_type": "id_drivers_license",
      "description": "back of driver's license"
    },
    "your_file_1": {
      "name": "CA drivers license",
      "filename": "img_201901022_193208",
      "hash": "246a9aaa83711158c3c4afa585a30be3bee8a34231ee72caac625faef48b4ace",
      "mime_type": "image/jpeg",
      "document_type": "id_drivers_license",
      "description": "full driver's license"
    }
  }
}
const document1 = {  
  filePath: '/path/to/file',  // In case of you have physical file path and using this SDK on server
  fileObject: 'file object', // In case of you not have physical file path and using this SDK on client
  fileBuffer: 'Uint8Array file buffer',// In case of you not have physical file path and using this SDK on client
  filename: 'file-name',    
  mimeType: 'image/png',    
  documentType: 'doc_green_card',   
  name: 'file name', // Optional    
  description: 'some file description', // Optional 
};

const document2 = {  
  filePath: '/path/to/file',  // In case of you have physical file path and using this SDK on server
  fileObject: 'file object', // In case of you not have physical file path and using this SDK on client
  fileBuffer: 'Uint8Array file buffer',// In case of you not have physical file path and using this SDK on client
  filename: 'file-name',    
  mimeType: 'image/png',    
  documentType: 'doc_green_card',   
  name: 'file name', // Optional    
  description: 'some file description', // Optional 
};
// fileObject and fileBuffer both are required if file send as object 
// OR filePath is required if not send fileObject and fileBuffer

// support multiple documents upload (pass array of document object)
const res = await sila.uploadDocuments(userHandle, userPrivateKey, [document1, document2]);    

// Success Response Object  
console.log(res.statusCode); // 200 
console.log(res.data.success); // true  
console.log(res.data.status); // SUCCESS    
console.log(res.data.message); // File uploaded successfully    
console.log(res.data.reference_id); // some-uuid-code   
console.log(res.data.document_id); // array of document_id
//Load your informations
string filepath0 = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"Resources{Path.DirectorySeparatorChar}logo-geko0.png");
string filepath1 = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"Resources{Path.DirectorySeparatorChar}logo-geko1.png");
           
var responseGetDocumentTypes = api.GetDocumentTypes();
var parsedResponse = (DocumentTypesResponse)responseGetDocumentTypes.Data;
var documentType = parsedResponse.DocumentTypes[0];

List<UploadDocument> uploadDocument = new List<UploadDocument>();
UploadDocument obj = new UploadDocument();
obj.FileName = "logo-geko0.png";
obj.FilePath = filepath0;
obj.Description = "test0";
obj.DocumentType = documentType.Name;
obj.MimeType = "image/png";
obj.Name = "logo-geko0";
uploadDocument.Add(obj);

obj = new UploadDocument();
obj.FileName = "logo-geko1.png";
obj.FilePath = filepath1;
obj.Description = "test1";
obj.DocumentType = documentType.Name;
obj.MimeType = "image/png";
obj.Name = "logo-geko1";
uploadDocument.Add(obj);


var response = api.UploadDocuments(userHandle, privateKey, uploadDocument);
           
// Success Object Response
Console.WriteLine(response.StatusCode); // 200  
var parsedResponseDoc = (UploadDocumentsResponse)response.Data;

Console.WriteLine(parsedResponseDoc.Success); // true
Console.WriteLine(parsedResponseDoc.Status); // SUCCESS
Console.WriteLine(parsedResponseDoc.Message); // File uploaded successfully
Console.WriteLine(parsedResponseDoc.ReferenceId); // some-uuid-code
Console.WriteLine(parsedResponseDoc.DocumentId); // array
Console.WriteLine(parsedResponseDoc.UnsuccessfulUploads); // array            
Console.WriteLine(parsedResponseDoc.ResponseTimeMs); // API responses time
ArrayList<UploadDocument> uploadDocumentList = new ArrayList<>();

uploadDocumentList.add(UploadDocument.builder()
          .filePath("/path/to/file_1") // Full path to the file
          .filename("logo-geko") // File name (without extension)
          .mimeType("image/png") // File mime-type
          .documentType("doc_green_card") // Document type
          .name("some file name") // Optional. Descriptive name of the document
          .description("some file description") // Optional. General description of the document_1
          .build());

uploadDocumentList.add(UploadDocument.builder()
          .filePath("/path/to/file_2") // Full path to the file
          .filename("tricolor") // File name (without extension)
          .mimeType("image/png") // File mime-type
          .documentType("doc_green_card") // Document type
          .name("some file name") // Optional. Descriptive name of the document
          .description("some file description") // Optional. General description of the document_2
          .build());

UploadDocumentsMessage message = UploadDocumentsMessage.builder()
          .userHandle("user_handle") // The user handle
          .userPrivateKey("user_private_key") // The user's private key
          .uploadDocumentList(uploadDocumentList).build();

ApiResponse response = api.uploadDocuments(message);

// Success response
System.out.println(response.getStatusCode()); // 200
UploadDocumentsResponse parsedResponse = (UploadDocumentsResponse) response.getData();
System.out.println(parsedResponse.getSuccess()); // true
System.out.println(parsedResponse.getStatus()); // SUCCESS
System.out.println(parsedResponse.getMessage()); // File uploaded successfully
System.out.println(parsedResponse.getDocumentIds());
System.out.println(parsedResponse.getReferenceId());
import hashlib

f = open(os.path.dirname(os.path.realpath(__file__)) +
                 "/images/logo-geko.png", "rb")
        fileContents_1 = f.read()
        f.close()

        a = open(os.path.dirname(os.path.realpath(__file__)) +
                 "/images/logo-geko.png", "rb")
        fileContents_2 = a.read()
        a.close() 

payload = {
            "user_handle": user_handle,
            "file_metadata" : {
                "file_1":{                    
                    "filename": "logo-geko1",
                    "hash": hashlib.sha256(fileContents_1).hexdigest(),
                    "mime_type": "image/png",
                    "document_type": "id_drivers_license",
                    "identity_type": "license",
                },
                "file_2":{
                    "filename": "logo-geko1",
                    "hash": hashlib.sha256(fileContents_2).hexdigest(),
                    "mime_type": "image/png",
                    "document_type": "id_drivers_license",
                    "identity_type": "license",
                }
            }
        }
        
        fileContent = {
            "file_1" : fileContents_1,
            "file_2" : fileContents_2
        }
response = silasdk.Documents.uploadDocuments(app, payload, fileContent, user_private_key)
$userHandle = 'user.silamoney.eth';
$privateKey = 'some private key';

$documents = [];        
$documents["file_1"] = [
    "filePath" => "/path/to/file",
    "filename" => "file-name",
    "mimeType" => "application/pdf",
    "documentType" => "id_drivers_permit"
];
$documents["file_2"] = [
    "filePath" => "/path/to/file",
    "filename" => "another-file-name",
    "mimeType" => "image/jpeg",
    "documentType" => "id_drivers_permit"
];
$response = self::$config->api->uploadDocuments($handle, $privateKey, $documents);

echo $response->getStatusCode(); // 200
echo $response->getData()->success; // TRUE
echo $response->getData()->status; // SUCCESS
echo $response->getData()->message; // File uploaded successfully.
echo $response->getData()->reference_id; // The reference uuid
echo $response->getData()->document_id; // The document uuid

Request Attributes

KeyTypeDescription
namestringRequired

Descriptive name of the document. (Max allowed length 100 chars)
file_namestringOptional.

Do not include the file extension. (Max allowed length 100 chars)
hashstringOptional.

SHA-256 hash of the file contents
mime_typestringOptional.

MIME type for one of Supported Image Formats
document typestringOptional.

One of Supported Document Types
descriptionstringRequired.

General description of the document. (Max allowed length 255 chars)

Journeys Specific Attributes

Disregard if using the Classic KYC flow.

KeyTypeDescription
verification_uuidstringRequired.

Verification UUID for the verification record the document is for. Can be found by calling /get_verifications.
providerstringOptional.

Defaults to ALLOY. Currently no other valid options.
is_frontbooleanRequired.

Defaults to true. Must be supplied and set to false for the back of documents.

Supported Image Formats

Image FormatImage ExtensionMIME type
PNG.pngimage/png
JPG.jpgimage/jpeg
PDF.pdfapplication/pdf

Supported Document Types

(see also /document_types)

Document Type
long-form (label)
Document Type short‑form (name)
1040 Tax Returntax_1040
Birth Certificatevtl_birth_certificate
Court Order for Name Changedoc_name_change
Divorce Decreevtl_divorce
Driver's permitid_drivers_permit
IRS form W2tax_w2
Lease agreementdoc_lease
Marriage Certificatevtl_marriage
Military Dependent's ID Cardid_military_dependent
Military IDid_military
Mortgage agreementdoc_mortgage
NYC ID Cardid_nyc_id
Other Documentother_doc
Other IDother_id
Passportid_passport
Pay Stub Documentationdoc_paystub
Permanent Resident Card (or Green Card)doc_green_card
Social Security Administration documentation (includes 4029)doc_ssa
Social Security Carddoc_ss_card
State ID cardid_state
State-issued driver's licenseid_drivers_license
Tax Form 1095tax_1095
Tax Form 1099tax_1099
Tuition Statementdoc_tuition
Unemployment Benefits Letterdoc_uo_benefits
US Passport Cardid_passport_card
Utility billdoc_utility
W4 Withholding Allowance Certificatetax_W4
Account Verification Letterverification_letter
Bank Statementbank_statement

Responses

Status CodesuccessDescription
200trueFile upload was successful
400falseDocument upload not permitted for users who have not started KYC
400falseUploaded file's signature does not match file_hash
400falseInvalid document_type
400falseUnsupported or mismatched mime_type

See Also:


What’s Next

The following are secondary endpoints that are either dependent on or used in conjunction with the primary /documents endpoint as well as other supporting documentation needed for triaging KYC failures.