Skip to main content

Push to HITL from API Deployments

Enterprise Feature

This feature is available in Unstract Cloud Edition and Unstract On-Premise Edition.

Push API deployment execution results directly to the HITL (Human-in-the-Loop) review queue for manual verification before final processing. This enables quality control, audit workflows, and selective human review of API-processed documents.

Overview

When executing an API deployment, you can optionally specify a hitl_queue_name parameter to route the execution results to a HITL review queue instead of returning them immediately. This allows:

  • Quality Assurance: Manual verification of extraction results before downstream processing
  • Audit Workflows: Human review of sensitive or high-value documents
  • Selective Review: Send only specific documents to HITL based on business logic
  • Exception Handling: Route problematic extractions for manual correction

How It Works

API Request → Document Processing → HITL Queue → Manual Review → Approval → Retrieve Results
↓ ↓ ↓ ↓ ↓ ↓
Send file Extract data Queue for review Reviewer edits Supervisor OK Get from queue

Normal API Flow:

POST /api → Process → Return Results

HITL-Enabled API Flow:

POST /api (with hitl_queue_name) → Process → Push to HITL Queue → Manual Review → Retrieve from Queue

API Usage

Endpoint

POST /deployment/api/{org_name}/{api_name}/

Request Parameters

ParameterTypeRequiredDescription
filesfile(s)YesDocument file(s) to process
timeoutintegerNoExecution timeout in seconds
include_metadatabooleanNoInclude processing metadata in response
hitl_queue_namestringNoHITL queue identifier for routing to review queue
Queue Naming

The hitl_queue_name acts as a custom identifier for your HITL queue. It's appended to the standard queue name format:

review_queue_{organization_id}_{workflow_id}:{hitl_queue_name}

Example Requests

Standard API Request (Without HITL)

curl --location 'https://us-central.unstract.com/deployment/api/myorg/invoice-api/' \
--header 'Authorization: Bearer <api_key>' \
--form 'files=@"invoice.pdf"' \
--form 'timeout="300"' \
--form 'include_metadata="true"'

API Request with HITL Queue

curl --location 'https://us-central.unstract.com/deployment/api/myorg/invoice-api/' \
--header 'Authorization: Bearer <api_key>' \
--form 'files=@"invoice.pdf"' \
--form 'timeout="300"' \
--form 'include_metadata="true"' \
--form 'hitl_queue_name="high-value-invoices"'

Multiple Files with HITL

curl --location 'https://us-central.unstract.com/deployment/api/myorg/invoice-api/' \
--header 'Authorization: Bearer <api_key>' \
--form 'files=@"invoice1.pdf"' \
--form 'files=@"invoice2.pdf"' \
--form 'files=@"invoice3.pdf"' \
--form 'hitl_queue_name="batch-review-001"'

Response Format

Success Response

{
"message": {
"status": "QUEUED",
"execution_id": "660e8400-e29b-41d4-a716-446655440001"
}
}

Response Fields:

FieldDescription
statusExecution status (QUEUED when using HITL)
execution_idUnique identifier for tracking this execution
note

When hitl_queue_name is provided, the API returns immediately with QUEUED status. Documents are processed and pushed to the HITL review queue. Results are available after manual review and approval via the Retrieve Approved Results API.

Error Responses

422 Unprocessable Entity

{
"error": "Invalid hitl_queue_name format"
}

Cause: Queue name doesn't meet validation requirements

403 Forbidden

{
"error": "HITL feature not available"
}

Cause: Enterprise license not active or HITL plugin not installed

406 Not Acceptable

{
"error": "Result already acknowledged"
}

Cause: Execution result has already been processed

Integration Examples

Python Example
import requests
import os

API_BASE_URL = "https://us-central.unstract.com"
ORG_NAME = "myorg"
API_NAME = "invoice-api"
API_KEY = os.environ.get("UNSTRACT_API_KEY")

def execute_with_hitl(file_path, hitl_queue_name):
"""Execute API deployment with HITL routing"""
url = f"{API_BASE_URL}/deployment/api/{ORG_NAME}/{API_NAME}/"

headers = {
"Authorization": f"Bearer {API_KEY}"
}

data = {
"timeout": 300,
"include_metadata": "true",
"hitl_queue_name": hitl_queue_name
}

with open(file_path, "rb") as f:
files = {"files": f}
response = requests.post(url, headers=headers, files=files, data=data)

response.raise_for_status()

result = response.json()
execution_id = result["message"]["execution_id"]

print(f"Document sent to HITL queue: {hitl_queue_name}")
print(f"Execution ID: {execution_id}")

return execution_id

# Execute with HITL
execution_id = execute_with_hitl("invoice.pdf", "high-value-invoices")

# Later, retrieve approved results using the Retrieve Approved Results API
# See: retrieve_approved_results.md
Python with Conditional HITL
def execute_api_with_conditional_hitl(file_path, amount):
"""Send to HITL only if amount exceeds threshold"""
url = f"{API_BASE_URL}/deployment/api/{ORG_NAME}/{API_NAME}/"

headers = {"Authorization": f"Bearer {API_KEY}"}

data = {
"timeout": 300,
"include_metadata": "true"
}

# Conditional HITL routing based on business logic
if amount > 10000:
data["hitl_queue_name"] = "high-value-invoices"
print(f"Amount ${amount} exceeds threshold, routing to HITL")
else:
print(f"Amount ${amount} below threshold, processing directly")

with open(file_path, "rb") as f:
response = requests.post(url, headers=headers, files={"files": f}, data=data)
return response.json()

# Usage
result = execute_api_with_conditional_hitl("invoice.pdf", amount=15000)
Node.js Example
const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');

const API_BASE_URL = "https://us-central.unstract.com";
const ORG_NAME = "myorg";
const API_NAME = "invoice-api";
const API_KEY = process.env.UNSTRACT_API_KEY;

async function executeWithHITL(filePath, hitlQueueName) {
const url = `${API_BASE_URL}/deployment/api/${ORG_NAME}/${API_NAME}/`;

const formData = new FormData();
formData.append('files', fs.createReadStream(filePath));
formData.append('timeout', '300');
formData.append('include_metadata', 'true');
formData.append('hitl_queue_name', hitlQueueName);

try {
const response = await axios.post(url, formData, {
headers: {
'Authorization': `Bearer ${API_KEY}`,
...formData.getHeaders()
}
});

const executionId = response.data.message.execution_id;
console.log(`Document sent to HITL queue: ${hitlQueueName}`);
console.log(`Execution ID: ${executionId}`);

return executionId;
} catch (error) {
console.error('Error:', error.response?.data || error.message);
throw error;
}
}

// Execute with HITL
executeWithHITL('invoice.pdf', 'high-value-invoices')
.then(executionId => {
console.log('Success:', executionId);
});
Batch Processing with Selective HITL
import requests
import os

def batch_process_with_selective_hitl(file_list, selection_criteria):
"""Process batch of files with selective HITL routing"""
url = f"{API_BASE_URL}/deployment/api/{ORG_NAME}/{API_NAME}/"
headers = {"Authorization": f"Bearer {API_KEY}"}

results = []

for file_info in file_list:
file_path = file_info["path"]
should_review = selection_criteria(file_info)

data = {"timeout": 300, "include_metadata": "true"}

if should_review:
# Route to HITL for manual review
data["hitl_queue_name"] = file_info.get("queue", "standard-review")
print(f"Sending {file_path} to HITL for review")
else:
print(f"Processing {file_path} directly")

with open(file_path, "rb") as f:
response = requests.post(url, headers=headers, files={"files": f}, data=data)
response_json = response.json()
results.append({
"file": file_path,
"execution_id": response_json.get("message", {}).get("execution_id") if should_review else None,
"hitl_enabled": should_review
})

return results

# Example usage
files = [
{"path": "invoice1.pdf", "amount": 15000, "queue": "high-value"},
{"path": "invoice2.pdf", "amount": 500, "queue": "standard"},
{"path": "invoice3.pdf", "amount": 50000, "queue": "high-value"},
]

# Criteria: Send to HITL if amount > $10,000
results = batch_process_with_selective_hitl(
files,
selection_criteria=lambda f: f["amount"] > 10000
)

print(f"Processed {len(results)} files")
print(f"Sent to HITL: {sum(1 for r in results if r['hitl_enabled'])}")

Configuration Requirements

Prerequisites

  1. Enterprise License: HITL queue management requires an active enterprise license

  2. API Deployment Created:

  3. HITL Configuration:

    • HITL plugin must be installed in the backend
    • Manual review workers must be configured
    • Redis connection for queue operations
  4. User Roles:

Validation Requirements

The hitl_queue_name parameter undergoes enterprise validation to ensure:

  • Valid format and characters
  • Compliance with queue naming conventions
  • No conflicts with system queue names

If validation fails, you'll receive a 422 Unprocessable Entity error.

Retrieving Results

Documents pushed to HITL queue follow this workflow:

  1. API Execution: Document processed and sent to HITL queue
  2. Manual Review: Reviewer examines and modifies extraction results
  3. Approval: Supervisor approves reviewed document (or auto-approved)
  4. Retrieval: Use Retrieve Approved Results API to get approved data

Retrieving Approved Results

After documents are approved, retrieve them using the approved results API:

import requests

# Retrieve approved results from HITL queue
def get_approved_results(org_name, class_id, hitl_queue_name):
"""Get approved results from HITL queue"""
url = f"{API_BASE_URL}/mr/api/{org_name}/approved/result/{class_id}/"

headers = {"Authorization": f"Bearer {API_KEY}"}
params = {"hitl_queue_name": hitl_queue_name}

response = requests.get(url, headers=headers, params=params)
response.raise_for_status()

return response.json()

# Get results
result = get_approved_results("myorg", "workflow-uuid", "high-value-invoices")
print(f"Approved result: {result['data']['result']}")

For complete details, see Retrieve Approved Results.

Best Practices

When to Use HITL from API

Good Use Cases:

  • High-Value Transactions: Invoices, contracts, or documents above a monetary threshold
  • Regulatory Compliance: Documents requiring audit trails and human verification
  • New Document Types: Testing extraction accuracy on unfamiliar formats
  • Exception Handling: Low-confidence extractions flagged for review
  • Quality Sampling: Random sampling for ongoing quality assurance

Avoid HITL For:

  • High-Volume Standard Docs: Well-tested, high-confidence extractions
  • Real-Time Requirements: When immediate results are needed
  • Fully Automated Workflows: Where human review adds no value

Queue Naming Conventions

  1. Use Descriptive Names: high-value-invoices, contract-review, qa-sample
  2. Avoid Special Characters: Use alphanumeric characters, hyphens, underscores
  3. Consistent Naming: Establish team conventions for queue names
  4. Purpose-Based Naming: Name queues by purpose, not by date/time

Error Handling

Python error handling example
def execute_with_error_handling(file_path, hitl_queue_name):
"""Execute API with comprehensive error handling"""
url = f"{API_BASE_URL}/deployment/api/{ORG_NAME}/{API_NAME}/"
headers = {"Authorization": f"Bearer {API_KEY}"}

data = {
"timeout": 300,
"hitl_queue_name": hitl_queue_name
}

try:
with open(file_path, "rb") as f:
response = requests.post(url, headers=headers, files={"files": f}, data=data)
response.raise_for_status()

result = response.json()
print(f"✓ Sent to HITL queue: {hitl_queue_name}")
return result["message"]["execution_id"]

except requests.exceptions.HTTPError as e:
if e.response.status_code == 422:
print(f"✗ Invalid queue name: {hitl_queue_name}")
elif e.response.status_code == 403:
print("✗ HITL feature not available (check enterprise license)")
else:
print(f"✗ HTTP error: {e}")
raise

except Exception as e:
print(f"✗ Unexpected error: {e}")
raise

Monitoring

  1. Track Queue Depth: Monitor how many documents are waiting for review
  2. Measure Review Time: Track time from API execution to approval
  3. Monitor Success Rate: Track approval vs. rejection rates
  4. Alert on Backlogs: Set up alerts when queue depth exceeds thresholds

Troubleshooting

Validation Error: Invalid hitl_queue_name

Error:

{
"error": "Invalid hitl_queue_name format"
}

Causes:

  • Queue name contains invalid characters
  • Queue name is too long
  • Queue name conflicts with system naming

Solutions:

  1. Use only alphanumeric characters, hyphens, and underscores
  2. Keep queue names under 50 characters
  3. Avoid reserved system keywords

HITL Feature Not Available

Error:

{
"error": "HITL feature not available"
}

Causes:

  • Enterprise license not active
  • HITL plugin not installed
  • Manual review backend not configured

Solutions:

  1. Verify enterprise license status
  2. Check that HITL plugin is installed in backend
  3. Ensure manual review workers are running
  4. Contact support if issue persists

Documents Not Appearing in Review Queue

Possible Causes:

  • Queue name mismatch
  • API execution failed
  • Workflow configuration issues
  • Redis connection problems

Solutions:

  1. Verify API execution completed successfully (check execution_id)
  2. Confirm queue name spelling matches exactly
  3. Check workflow logs for errors
  4. Verify Redis connection and queue status

Cannot Retrieve Approved Results

Possible Causes:

  • Documents not yet approved
  • Wrong class_id or org_name
  • Queue name parameter missing in retrieval
  • API key lacks permissions

Solutions:

  1. Check HITL review interface to confirm documents are approved
  2. Verify class_id matches your workflow
  3. Include hitl_queue_name parameter in retrieval request if used
  4. Confirm API key has Human Review permissions

Workflow Combinations

HITL + Auto Approval

Combine API HITL routing with Auto Approval for trusted reviewers:

API (hitl_queue_name) → HITL Queue → Reviewer Review → Auto Approve → Retrieve Results

Benefit: Documents still get reviewed, but trusted reviewers' submissions skip supervisor approval.

HITL + Destination DB

Configure workflow to send approved results to destination database:

API (hitl_queue_name) → HITL Queue → Review → Approve → Destination DB

Benefit: Approved results automatically inserted into your database.

FAQ

Can I use different queue names for different document types?

Yes! Use different hitl_queue_name values to route different document types to separate queues for organized review.

What happens if I don't provide hitl_queue_name?

The API processes the document normally and returns results immediately without HITL routing.

Can I use HITL with batch API executions?

Yes, include hitl_queue_name in batch requests. All files in the batch will be routed to the specified HITL queue.

How do I know when documents are approved?

Poll the Retrieve Approved Results API or set up webhooks for approval notifications.

Can reviewers see the hitl_queue_name in the interface?

Yes, reviewers can see which queue they're working from in the Human Quality Review interface.

Is there a limit to queue name length?

Queue names should be kept concise (under 50 characters recommended) for optimal performance.

Can multiple APIs share the same hitl_queue_name?

Yes, but be cautious - reviewers will see mixed documents from different APIs in the same queue.