Integration guides
Complete working examples for integrating the Vindonissa API in your application. Each example covers the full workflow: submit a document, poll for the result, and extract the data.
Full workflow
Select a language to see the complete integration example.
import base64
import time
import requests
API_KEY = "vnd_your_api_key"
BASE_URL = "https://api.helvetii.ai"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
def process_document(file_path: str, project: str, document_type: str) -> dict:
"""Submit a document and wait for the result."""
with open(file_path, "rb") as f:
file_b64 = base64.b64encode(f.read()).decode()
# Submit
resp = requests.post(
f"{BASE_URL}/process_file",
headers=HEADERS,
json={
"file_base64": file_b64,
"project": project,
"document_type": document_type,
},
)
resp.raise_for_status()
task_id = resp.json()["task_id"]
# Poll
while True:
status = requests.get(
f"{BASE_URL}/task_status/{task_id}",
headers=HEADERS,
).json()
if status["status"] == "complete":
return status["result"]
if status["status"] == "failed":
raise RuntimeError(status["error"])
time.sleep(2)
result = process_document("invoice.pdf", "my_project", "invoice")
print(result)const API_KEY = "vnd_your_api_key";
const BASE_URL = "https://api.helvetii.ai";
async function processDocument(
file: File,
project: string,
documentType: string
): Promise<Record<string, unknown>> {
const buffer = await file.arrayBuffer();
const fileBase64 = btoa(
String.fromCharCode(...new Uint8Array(buffer))
);
// Submit
const submitRes = await fetch(`${BASE_URL}/process_file`, {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
file_base64: fileBase64,
project,
document_type: documentType,
}),
});
if (!submitRes.ok) throw new Error(await submitRes.text());
const { task_id } = await submitRes.json();
// Poll
while (true) {
const statusRes = await fetch(
`${BASE_URL}/task_status/${task_id}`,
{ headers: { Authorization: `Bearer ${API_KEY}` } }
);
const status = await statusRes.json();
if (status.status === "complete") return status.result;
if (status.status === "failed") throw new Error(status.error);
await new Promise((r) => setTimeout(r, 2000));
}
}# Step 1: Submit the document
TASK_ID=$(curl -s -X POST https://api.helvetii.ai/process_file \
-H "Authorization: Bearer vnd_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"file_base64": "'$(base64 -i invoice.pdf)'",
"project": "my_project",
"document_type": "invoice"
}' | jq -r '.task_id')
echo "Task ID: $TASK_ID"
# Step 2: Poll until complete
while true; do
RESULT=$(curl -s https://api.helvetii.ai/task_status/$TASK_ID \
-H "Authorization: Bearer vnd_your_api_key")
STATUS=$(echo $RESULT | jq -r '.status')
if [ "$STATUS" = "complete" ]; then
echo $RESULT | jq '.result'
break
elif [ "$STATUS" = "failed" ]; then
echo "Error:" $(echo $RESULT | jq -r '.error')
break
fi
sleep 2
donepackage main
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
)
const (
apiKey = "vnd_your_api_key"
baseURL = "https://api.helvetii.ai"
)
func processDocument(filePath, project, docType string) (map[string]any, error) {
data, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
body, _ := json.Marshal(map[string]any{
"file_base64": base64.StdEncoding.EncodeToString(data),
"project": project,
"document_type": docType,
})
req, _ := http.NewRequest("POST", baseURL+"/process_file", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var submitResult struct{ TaskID string `json:"task_id"` }
json.NewDecoder(resp.Body).Decode(&submitResult)
// Poll for result
for {
req, _ := http.NewRequest("GET",
fmt.Sprintf("%s/task_status/%s", baseURL, submitResult.TaskID), nil)
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
respBody, _ := io.ReadAll(resp.Body)
resp.Body.Close()
var status struct {
Status string `json:"status"`
Result map[string]any `json:"result"`
Error string `json:"error"`
}
json.Unmarshal(respBody, &status)
switch status.Status {
case "complete":
return status.Result, nil
case "failed":
return nil, fmt.Errorf("task failed: %s", status.Error)
}
time.Sleep(2 * time.Second)
}
}
func main() {
result, err := processDocument("invoice.pdf", "my_project", "invoice")
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(result)
}<?php
$apiKey = "vnd_your_api_key";
$baseUrl = "https://api.helvetii.ai";
function processDocument(string $filePath, string $project, string $docType): array
{
global $apiKey, $baseUrl;
$fileB64 = base64_encode(file_get_contents($filePath));
// Submit
$ch = curl_init("$baseUrl/process_file");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer $apiKey",
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"file_base64" => $fileB64,
"project" => $project,
"document_type" => $docType,
]),
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
$taskId = $response["task_id"];
// Poll
while (true) {
$ch = curl_init("$baseUrl/task_status/$taskId");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer $apiKey"],
]);
$status = json_decode(curl_exec($ch), true);
curl_close($ch);
if ($status["status"] === "complete") {
return $status["result"];
}
if ($status["status"] === "failed") {
throw new RuntimeException($status["error"]);
}
sleep(2);
}
}
$result = processDocument("invoice.pdf", "my_project", "invoice");
print_r($result);How it works
- 1Read and encode — Load the PDF or image file and base64-encode its binary content.
- 2Submit — POST the encoded file to
/process_filewith your project and document type. The API returns atask_id. - 3Poll — GET
/task_status/{task_id}every 2 seconds untilstatusiscompleteorfailed. - 4Extract — Read the structured data from the
resultfield.
Error handling
The API returns standard HTTP status codes. All error responses include a detail field with a human-readable message.
| Code | Handling |
|---|---|
| 401 | Check API key format and validity. Keys use vnd_ prefix. |
| 402 | Subscription inactive. Renew or contact support. |
| 404 | Project or document type not found. Verify the ID or name. |
| 422 | Validation error. Check required fields and data formats. |
| 500 | Server error. Retry with exponential backoff. |
Tip
For production use, implement retries with exponential backoff for 5xx errors. A good starting point is 3 attempts with 1s, 2s, and 4s delays.