Uploading Documents via API

This integration pattern guides you through the process of uploading documents to Kompliant and optionally linking them to workflow requirements.

Overview

A document upload workflow in Kompliant involves the following steps:

  1. Create a document record and receive an upload URL
  2. Upload the file using the URL
  3. Link the document to workflow requirements (optional)

This guide walks you through the sequence of API calls needed to complete these steps.

Step 1: Create the Document Record

The first step is to create a document record using the document.create endpoint. This will generate an upload URL that you'll use to upload the actual file.

POST https://api.kompliant.com/document.create

Request Body:

{
  "subject_record_id": "sr_1rQYvnUQdcatd4TcZQu0l8",
  "file_name": "passport.pdf",
  "file_size_in_bytes": 1048576
}

📘

Note

The file_name should include the file extension such as .pdf, .jpg, or .png). The system validates that the file type against the DOCUMENT_FILE_TYPES.

Response:

{
 "document": {
  "document_id": "d_1rQYvnUQdcatd4TcZQu0l8",
  "upload_url": "https://dev-kompliant-document-management-20250212155721984000000001.s3.us-east-1.amazonaws.com",
  "presign_values": {
    "Content-Type": "application/pdf",
    "X-Amz-Algorithm": "AWS4-HMAC-SHA256",
    "X-Amz-Credential": "ASIAQHNPJKZEMJ42PUB3/20251003/us-east-1/s3/aws4_request",
    "X-Amz-Date": "20251003T220920Z",
    "X-Amz-Security-Token": "AMZ_SECURITY_TOKEN",
    "X-Amz-Signature": "AMX_SIGNATURE",
    "key": "sr_1rQYvnUQdcatd4TcZQu0l8/d_1rQYvnUQdcatd4TcZQu0l8.pdf",
    "policy": "AMZ_POLICY_JWT",
    "x-amz-meta-document-id": "d_1rQYvnUQdcatd4TcZQu0l8"
  }
}

Save the document_id from the response - you'll need it if you want to link this document to workflow requirements in Step 3.

For more details on this API, see document.create.

Step 2: Upload the File

Using the upload_url and presign_values from the previous step, perform a multipart/form-data POST request to upload your file.

⚠️

Important

The presigned URL has an expiration time of five (5) minutes. Make sure to upload the file promptly after creating the document record.

2.1 Examples

POST / HTTP/1.1
Host: kompliant-document-management.s3.us-east-1.amazonaws.com
Content-Length: 4067
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="Content-Type"

application/pdf
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="key"

sr_1rQYvnUQdcatd4TcZQu0l8/d_1rQYvnUQdcatd4TcZQu0l8.pdf
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="policy"

AMZ_POLICY_BASE64_ENCODED_STRING
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="X-Amz-Algorithm"

AWS4-HMAC-SHA256
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="X-Amz-Credential"

AMZ_ACCESS_KEY_ID/20251003/us-east-1/s3/aws4_request
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="X-Amz-Date"

20251003T220920Z
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="X-Amz-Security-Token"

AMZ_SECURITY_TOKEN
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="X-Amz-Signature"

AMZ_SIGNATURE
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="x-amz-meta-document-id"

d_1rQYvnUQdcatd4TcZQu0l8
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="passport.pdf"
Content-Type: application/pdf

(binary data)
------WebKitFormBoundary7MA4YWxkTrZu0gW--
curl -X POST "https://kompliant-document-management.s3.us-east-1.amazonaws.com" \
  -F "Content-Type=application/pdf" \
  -F "key=sr_1rQYvnUQdcatd4TcZQu0l8/d_1rQYvnUQdcatd4TcZQu0l8.pdf" \
  -F "policy=AMZ_POLICY_BASE64_ENCODED_STRING" \
  -F "X-Amz-Algorithm=AWS4-HMAC-SHA256" \
  -F "X-Amz-Credential=AMZ_ACCESS_KEY_ID/20251003/us-east-1/s3/aws4_request" \
  -F "X-Amz-Date=20251003T220920Z" \
  -F "X-Amz-Security-Token=AMZ_SECURITY_TOKEN" \
  -F "X-Amz-Signature=AMZ_SIGNATURE" \
  -F "x-amz-meta-document-id=d_1rQYvnUQdcatd4TcZQu0l8" \
  -F "file=@/path/to/passport.pdf"
// Assume you have the response from document.create stored in 'createResponse'
const { upload_url, presign_values, document_id } = createResponse.data.document;

// Create FormData with presign values
const formData = new FormData();

// Add all presign_values as form fields (ORDER MATTERS - add these first)
formData.append("Content-Type", presign_values["Content-Type"]);
formData.append("key", presign_values["key"]);
formData.append("policy", presign_values["policy"]);
formData.append("X-Amz-Algorithm", presign_values["X-Amz-Algorithm"]);
formData.append("X-Amz-Credential", presign_values["X-Amz-Credential"]);
formData.append("X-Amz-Date", presign_values["X-Amz-Date"]);
formData.append("X-Amz-Security-Token", presign_values["X-Amz-Security-Token"]);
formData.append("X-Amz-Signature", presign_values["X-Amz-Signature"]);
formData.append("x-amz-meta-document-id", presign_values["x-amz-meta-document-id"]);

// Add the file as the LAST field
formData.append("file", fileInput.files[0]);

// Upload file
const requestOptions = {
  method: "POST",
  body: formData,
  redirect: "follow"
};

fetch(upload_url, requestOptions)
  .then((response) => {
    if (response.ok) {
      console.log("File uploaded successfully!");
      console.log("Document ID:", document_id);
      // Proceed to Step 3 if you want to link to workflow requirements
    } else {
      console.error("Upload failed:", response.status);
    }
  })
  .catch((error) => console.error("Upload error:", error));
import requests

# Assume you have the response from document.create
upload_url = response_data["data"]["document"]["upload_url"]
presign_values = response_data["data"]["document"]["presign_values"]
document_id = response_data["data"]["document"]["document_id"]

# Open the file
with open("passport.pdf", "rb") as file:
    # Create multipart form data with presign values
    files = {
        "Content-Type": (None, presign_values["Content-Type"]),
        "key": (None, presign_values["key"]),
        "policy": (None, presign_values["policy"]),
        "X-Amz-Algorithm": (None, presign_values["X-Amz-Algorithm"]),
        "X-Amz-Credential": (None, presign_values["X-Amz-Credential"]),
        "X-Amz-Date": (None, presign_values["X-Amz-Date"]),
        "X-Amz-Security-Token": (None, presign_values["X-Amz-Security-Token"]),
        "X-Amz-Signature": (None, presign_values["X-Amz-Signature"]),
        "x-amz-meta-document-id": (None, presign_values["x-amz-meta-document-id"]),
        "file": file
    }
    
    # Upload file
    response = requests.post(upload_url, files=files)
    
    if response.status_code == 204:
        print(f"File uploaded successfully! Document ID: {document_id}")
        # Proceed to Step 3 if you want to link to workflow requirements
    else:
        print(f"Upload failed: {response.status_code}")

📘

Upload Success

A successful upload returns an HTTP status code 204 No Content. The document record in Kompliant will be automatically updated with the upload completion status.

Step 3: Link Document to Workflow Requirements (Optional)

After uploading a document, you can link it to one or more Required Documents in a workflow by creating Fulfillments. This step is optional but commonly used when documents are being collected as part of an BUSINESS_APPLICATION, BUSINESS_UNDERWRITING or ACCOUNT_PROVISIONING workflow.

Use Cases

Link documents to workflow requirements when:

  • Uploading documents that fulfill specific workflow document requirements
  • The same document satisfies multiple document requirements (e.g., a bank statement that serves as both proof of address and proof of account)
  • Building an application where users upload documents to complete their workflow

3.1 Retrieve Required Documents for Workflow

First, get the list of Required Documents for your workflow to find the required_document_id values you need:

POST https://api.kompliant.com/workflow.requiredDocument.list

Request Body:

{
  "workflow_id": "w_LKQpQvG7r6BWXBzrvFvI5"
}

Response:

"required_documents": [
  {
    "required_document_id": "rd_9K3mPxQvN2jL7hR5TcWfY8",
    "document_type_code": "PASSPORT",
    "name": "Passport Document",
    "category": "REQUIREMENT",
    "requirement_text": "Valid passport required",
    "required_document_status": "INACTIVE",
    "created_at": "2025-01-01T00:00:00Z",
    "updated_at": "2025-01-01T00:00:00Z"
  },
  {
    "required_document_id": "rd_7P2nQwRxM8kN4jS6UdXgZ9",
    "document_type_code": "BANK_STATEMENT",
    "name": "Bank Statement",
    "category": "REQUIREMENT",
    "requirement_text": "Most recent 3 months",
    "required_document_status": "INACTIVE",
    "created_at": "2025-01-01T00:00:00Z",
    "updated_at": "2025-01-01T00:00:00Z"
  }
]

For more details on this API, see workflow.requiredDocument.list.

3.2 Create Fulfillments to Link Documents

Use the workflow.requiredDocument.fulfillment.batchAdd endpoint to link your uploaded document to one or more Required Documents:

POST https://api.kompliant.com/workflow.requiredDocument.fulfillment.batchAdd

Request Body:

{
  "workflow_id": "w_LKQpQvG7r6BWXBzrvFvI5",
  "fulfillments": [
    {
      "required_document_id": "rd_9K3mPxQvN2jL7hR5TcWfY8",
      "document_id": "d_1rQYvnUQdcatd4TcZQu0l8"
    }
  ]
}

📘

Multiple Fulfillments

You can link the same document to multiple Required Documents by adding multiple objects to the fulfillments array. This is useful when a single document satisfies multiple requirements such as a driver's license serving as both ID verification and proof of address.

Example - One Document for Multiple Requirements:

{
  "workflow_id": "w_LKQpQvG7r6BWXBzrvFvI5",
  "fulfillments": [
    {
      "required_document_id": "rd_9K3mPxQvN2jL7hR5TcWfY8",
      "document_id": "d_1rQYvnUQdcatd4TcZQu0l8"
    },
    {
      "required_document_id": "rd_7P2nQwRxM8kN4jS6UdXgZ9",
      "document_id": "d_1rQYvnUQdcatd4TcZQu0l8"
    }
  ]
}

Response:

"fulfillments": [
  {
    "fulfillment_id": "f_1rQYvnUQdcatd4TcZQu0l8",
    "required_document_id": "rd_9K3mPxQvN2jL7hR5TcWfY8",
    "document_id": "d_1rQYvnUQdcatd4TcZQu0l8",
    "decision_status": "PENDING",
    "created_at": "2025-01-01T00:00:00Z",
    "updated_at": "2025-01-01T00:00:00Z"
  }
]

For more details on this API, see workflow.requiredDocument.fulfillment.batchAdd.

Complete Integration Flow

Below is a flowchart of the complete integration pattern:

document.create
    ↓
Receive document_id, upload_url, and presign_values
    ↓
Upload file using presign_values
    ↓
[Optional] workflow.requiredDocument.list
    ↓
[Optional] workflow.requiredDocument.fulfillment.batchAdd

Best Practices

  1. Error Handling: Implement proper error handling for each step:

    • Check for validation errors in document.create (invalid file types, missing fields)
    • Monitor upload response (204 = success)
    • Handle fulfillment creation errors (invalid IDs, workflow already completed)
  2. File Validation: Before calling document.create, validate:

    • File extension is in the allowed list
    • File size matches the file_size_in_bytes parameter
    • File name is correctly formatted
  3. Upload URL Expiration: The upload URL typically expires after 5 minutes. If you need to retry the upload:

    • Call document.create again to get a new upload URL
    • You will need to use the newly created document record
  4. ID Management: Maintain a mapping of IDs created during the workflow:

    • subject_record_id from workflow creation
    • document_id from document.create
    • required_document_id from workflow.requiredDocument.list
    • fulfillment_id from fulfillment creation
  5. One Document, Multiple Fulfillments: When a single document satisfies multiple requirements:

    • Upload the document once using document.create and upload URL
    • Create multiple fulfillment entries in the fulfillments array
    • This maintains storage efficiency while properly tracking requirement fulfillment
  6. Upload Verification: After file upload, you can verify the document was properly uploaded by:

    • Checking for HTTP 204 response from POST call
    • Using document.get to retrieve document details and verify upload_status
    • Using workflow.requiredDocument.fulfillment.list to verify fulfillments were created
  7. Content-Type Enforcement: The upload URL enforces content-type matching:

    • Ensure the file being uploaded matches the content type derived from file_name
    • The upload will fail if there's a mismatch between the file extension and actual file type