Evidence Sheet Guide¶
Evidence sheets provide complete audit trails for legal compliance, including a comprehensive timeline of all signature events, signer authentication details, and cryptographic verification for legal validity.
Optimized Performance
The evidence endpoint uses intelligent async caching for 20-50x faster response times on subsequent requests.
Overview¶
Evidence sheets are legally binding PDF documents that include:
- Complete timeline of all envelope events with timestamps
- IP addresses and geolocation data for each action
- Email delivery confirmations and read receipts
- Signature verification with cryptographic details
- Authentication records including 2FA validation attempts
- Cryptographic hashes for document integrity
- Legal compliance information and audit trail
How Evidence Generation Works¶
Async Caching Architecture¶
The evidence endpoint uses a smart caching pattern for optimal performance:
| Scenario | Response Time | Behavior |
|---|---|---|
| First Request | 1-3 seconds | Generates evidence synchronously |
| Subsequent Requests | <100ms | Returns cached URL immediately (20-50x faster!) |
| Background Updates | Non-blocking | Auto-regenerates when timeline changes |
Automatic Generation¶
Evidence sheets are automatically generated when:
- ✅ Envelope status changes to
SUCCESS - ✅ Timeline has new events since last generation
- ✅ Manual regeneration is triggered
Evidence is NOT regenerated when timeline is unchanged, avoiding unnecessary processing.
Getting Evidence Sheets¶
Basic Request¶
Once an envelope reaches SUCCESS status:
Success Response¶
{
"success": true,
"evidence_document_url": "https://s3.amazonaws.com/.../evidence_sheet.pdf",
"request_id": "req-001"
}
The evidence_document_url is a direct download link to the PDF file stored in S3.
Error Responses¶
// Envelope not completed yet
{
"error": "Envelope not in SUCCESS status",
"request_id": "req-002"
}
// Envelope not found or unauthorized
{
"error": "Envelope not found",
"request_id": "req-003"
}
// Evidence generation failed
{
"success": false,
"evidence_document_url": null,
"request_id": "req-004"
}
Downloading the PDF¶
Option 1: Direct Download¶
The evidence_document_url can be used directly in a browser or download tool:
# Download with curl
curl -o evidence.pdf "https://s3.amazonaws.com/.../evidence_sheet.pdf"
# Download with wget
wget -O evidence.pdf "https://s3.amazonaws.com/.../evidence_sheet.pdf"
Option 2: Programmatic Download¶
import requests
# Get evidence URL
response = requests.get(f"{API_BASE}/envelopes/{envelope_id}/evidence", headers=headers)
evidence_url = response.json()["evidence_document_url"]
# Download PDF
pdf_response = requests.get(evidence_url)
with open(f"evidence_{envelope_id}.pdf", "wb") as f:
f.write(pdf_response.content)
print(f"Evidence saved to evidence_{envelope_id}.pdf")
// Get evidence URL
const response = await fetch(`${API_BASE}/envelopes/${envelopeId}/evidence`, {
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
const { evidence_document_url } = await response.json();
// Download PDF
const pdfResponse = await fetch(evidence_document_url);
const blob = await pdfResponse.blob();
// Save to file (Node.js)
const fs = require('fs');
const buffer = await blob.arrayBuffer();
fs.writeFileSync(`evidence_${envelopeId}.pdf`, Buffer.from(buffer));
require 'net/http'
require 'json'
# Get evidence URL
uri = URI("#{API_BASE}/envelopes/#{envelope_id}/evidence")
request = Net::HTTP::Get.new(uri)
request['Authorization'] = "Bearer #{API_KEY}"
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
evidence_url = JSON.parse(response.body)['evidence_document_url']
# Download PDF
pdf_content = Net::HTTP.get(URI(evidence_url))
File.write("evidence_#{envelope_id}.pdf", pdf_content)
puts "Evidence saved to evidence_#{envelope_id}.pdf"
Best Practices¶
1. Check Envelope Status First¶
Always verify the envelope is complete before requesting evidence:
# Get envelope status
response = requests.get(f"{API_BASE}/envelopes/{envelope_id}", headers=headers)
envelope = response.json()["envelope"]
# Only request evidence if SUCCESS
if envelope["status"] == "SUCCESS":
evidence_response = requests.get(
f"{API_BASE}/envelopes/{envelope_id}/evidence",
headers=headers
)
else:
print(f"Envelope not ready: {envelope['status']}")
2. Cache Evidence URLs¶
Evidence URLs are stable and can be cached locally:
# Store URL for later use
evidence_cache[envelope_id] = evidence_url
# No need to poll the endpoint repeatedly
# Just download from cached URL when needed
3. Handle Errors Gracefully¶
Implement proper error handling:
try:
response = requests.get(f"{API_BASE}/envelopes/{envelope_id}/evidence", headers=headers)
response.raise_for_status()
data = response.json()
if data.get("success"):
return data["evidence_document_url"]
else:
# Generation failed
logger.error(f"Evidence generation failed for {envelope_id}")
return None
except requests.HTTPError as e:
if e.response.status_code == 405:
# Envelope not ready yet
logger.info(f"Envelope {envelope_id} not completed")
elif e.response.status_code == 404:
# Envelope not found
logger.error(f"Envelope {envelope_id} not found")
return None
4. Download PDF Immediately¶
Don't rely on long-term URL storage:
# GOOD: Download and store PDF
evidence_url = get_evidence_url(envelope_id)
pdf_content = download_pdf(evidence_url)
store_in_database(envelope_id, pdf_content)
# BAD: Only store URL long-term
evidence_url = get_evidence_url(envelope_id)
store_in_database(envelope_id, evidence_url) # URL might change!
Legal Compliance¶
Evidence sheets are legally binding and compliant with:
- Chilean Law 19.799 - Electronic Documents and Digital Signatures
- ESIGN Act - United States Electronic Signatures in Global and National Commerce Act
- eIDAS Regulation - European Union Electronic Identification and Trust Services
Evidence sheets include:
- Complete audit trail with timestamps
- Cryptographic verification details
- Signer authentication records
- Document integrity validation
- IP addresses and geolocation data
Troubleshooting¶
"Envelope not in SUCCESS status" Error¶
Cause: Envelope is still in progress or failed.
Solution: Check envelope status and wait for completion:
envelope = get_envelope(envelope_id)
print(f"Status: {envelope['status']}")
# Wait until status is SUCCESS
Slow Response Times¶
Cause: First request generates evidence synchronously.
Solution: This is expected behavior. Subsequent requests will be fast (<100ms).
Evidence Missing Recent Events¶
Cause: Evidence was cached before latest events.
Solution: Wait 30-60 seconds and request again. Background update will have completed.
Generation Failed (500 Error)¶
Cause: PDF generation or S3 upload failed.
Solution: - Retry the request after a few seconds - Check envelope has valid timeline data - Contact support if issue persists
Related Documentation¶
- Envelopes API Reference - Complete envelope endpoint documentation
- Webhooks Guide - Get notified when envelope completes
- API Overview - General API conventions
Performance Tips¶
For High-Volume Applications¶
- Use Webhooks: Get notified when envelope completes instead of polling
- Cache Locally: Store evidence URLs to avoid repeated API calls
- Batch Downloads: Download multiple evidence sheets in parallel
- Monitor Workers: Track evidence generation metrics
Expected Performance¶
- First evidence request: 1-3 seconds (generation time)
- Cached requests: <100ms (20-50x faster)
- Background updates: Non-blocking, happens asynchronously
Example: Complete Workflow¶
import requests
import time
API_BASE = "https://api.lexgo.cl/api/v1"
headers = {"Authorization": f"Bearer {API_KEY}"}
def wait_for_envelope_completion(envelope_id, timeout=3600):
"""Wait for envelope to reach SUCCESS status."""
start_time = time.time()
while time.time() - start_time < timeout:
response = requests.get(f"{API_BASE}/envelopes/{envelope_id}", headers=headers)
envelope = response.json()["envelope"]
if envelope["status"] == "SUCCESS":
return True
elif envelope["status"] in ["VOIDED", "EXPIRED"]:
return False
time.sleep(30) # Poll every 30 seconds
return False
def download_evidence(envelope_id):
"""Download evidence sheet for completed envelope."""
# Get evidence URL
response = requests.get(
f"{API_BASE}/envelopes/{envelope_id}/evidence",
headers=headers
)
if response.status_code == 200:
data = response.json()
if data["success"]:
evidence_url = data["evidence_document_url"]
# Download PDF
pdf_response = requests.get(evidence_url)
filename = f"evidence_{envelope_id}.pdf"
with open(filename, "wb") as f:
f.write(pdf_response.content)
print(f"✓ Evidence downloaded: {filename}")
return filename
return None
# Complete workflow
envelope_id = "abc-123"
print(f"Waiting for envelope {envelope_id} to complete...")
if wait_for_envelope_completion(envelope_id):
print("✓ Envelope completed successfully")
evidence_file = download_evidence(envelope_id)
print(f"✓ Evidence saved to: {evidence_file}")
else:
print("✗ Envelope did not complete")