Status Tracking
Document processing is an asynchronous process. Therefore, we need a way to track the status of the processing. There are 2 ways to track the status of the processing:
- WebSockets (recommended)
- Polling (alternative)
WebSockets
Section titled “WebSockets”WebSockets are a more efficient way to track the status of the processing. It is a long-lived connection that allows you to receive updates as they happen.
import asyncioimport websocketsfrom pydantic import BaseModel
BASE_URL = 'ws://dev-api.v2.areal.ai/'
async def get_status(client: websockets.WebSocketClientProtocol, document_id: str) -> str: async with websockets.connect(f"{BASE_URL}/status/") as ws: while True: message = await ws.recv() msg = FileStatusUpdatedMessage.model_validate_json(message)
# skip if not the document we are interested in if msg.meta.id != document_id: continue
print(f"Processing {msg.meta.id} is {msg.data.status}")
# NOTE: no need to break since if this is background task # and so you can keep listening for other documents
asyncio.run(get_status(document_id))
class FileStatusUpdatedMessage(BaseModel): class Meta(BaseModel): upload_session_id: str id: str user: User
class Data(BaseModel): status: Status
type: str = 'FILE_STATUS_UPDATED' meta: Meta data: Datausing System;using System.Net.WebSockets;using System.Text;using System.Text.Json;using System.Threading;using System.Threading.Tasks;
var baseUrl = "ws://dev-api.v2.areal.ai/";var documentId = "your_document_id_here";
using var client = new ClientWebSocket();await client.ConnectAsync(new Uri(baseUrl + "status/"), CancellationToken.None);
var buffer = new byte[1024 * 4];
while (client.State == WebSocketState.Open){ var result = await client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Text) { var message = Encoding.UTF8.GetString(buffer, 0, result.Count); var jsonDoc = JsonDocument.Parse(message); var root = jsonDoc.RootElement;
var meta = root.GetProperty("meta");
// skip if not the document we are interested in if (meta.GetProperty("id").GetString() != documentId) { continue; }
var data = root.GetProperty("data"); var status = data.GetProperty("status").GetString(); Console.WriteLine($"Processing {meta.GetProperty("id").GetString()} is {status}");
// NOTE: no need to break since if this is background task // and so you can keep listening for other documents } else if (result.MessageType == WebSocketMessageType.Close) { await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); }}import java.net.URI;import java.net.http.HttpClient;import java.net.http.WebSocket;import java.util.concurrent.CompletionStage;import org.json.JSONObject;
public class WebSocketStatusTracking { public static void main(String[] args) throws Exception { String baseUrl = "ws://dev-api.v2.areal.ai/"; String documentId = "your_document_id_here";
HttpClient client = HttpClient.newHttpClient(); WebSocket webSocket = client.newWebSocketBuilder() .buildAsync(URI.create(baseUrl + "status/"), new WebSocket.Listener() { @Override public void onOpen(WebSocket webSocket) { System.out.println("WebSocket connection opened"); webSocket.request(1); return WebSocket.Listener.super.onOpen(webSocket); }
@Override public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) { try { JSONObject message = new JSONObject(data.toString()); JSONObject meta = message.getJSONObject("meta");
// skip if not the document we are interested in if (!meta.getString("id").equals(documentId)) { webSocket.request(1); return null; }
JSONObject messageData = message.getJSONObject("data"); String status = messageData.getString("status"); System.out.println("Processing " + meta.getString("id") + " is " + status);
// NOTE: no need to break since if this is background task // and so you can keep listening for other documents } catch (Exception e) { e.printStackTrace(); } webSocket.request(1); return null; }
@Override public void onError(WebSocket webSocket, Throwable error) { System.err.println("WebSocket error: " + error.getMessage()); } }) .join();
// Keep the connection alive Thread.sleep(Long.MAX_VALUE); }}For other WebSocket notification types see WebSocket API
Polling
Section titled “Polling”Continuously poll the status of the processing until it is completed or failed. You can also implement a custom timeout to avoid waiting forever.
import requestsimport time
BASE_URL = 'http://dev-api.v2.areal.ai/api/v2'
# 0. Login - details in Authentication section ... client is authenticated# 1. Starting the processing ... we get a request_id (which is actually the document_id) -- details in Processing section# 2. Polling the status of the processing
document_id = "your_document_id_here"
def get_status(client: requests.Session, document_id: str) -> str: try: response = client.get(f"{BASE_URL}/documents/{document_id}/") document_details = response.json() return document_details["status"] except Exception as e: print(f"Error getting status: {e}") return "unknown"
status = get_status(client, document_id)# NOTE: You can also inspect other fields in document_details as needed
timeout = 10while status not in ["completed", "failed"]: time.sleep(1) timeout -= 1 if timeout <= 0: print("Timeout reached") break status = get_status(client, document_id)using System;using System.Net.Http;using System.Threading.Tasks;using System.Text.Json;using System.Net;using System.Threading;
var baseUrl = "http://dev-api.v2.areal.ai/api/v2";
// 0. Login - details in Authentication section ... client is authenticated// 1. Starting the processing ... we get a request_id (which is actually the document_id) -- details in Processing section// 2. Polling the status of the processing
var documentId = "your_document_id_here";
var handler = new HttpClientHandler{ UseCookies = true, CookieContainer = new CookieContainer()};var client = new HttpClient(handler);
string status = await GetStatus(client, baseUrl, documentId);// NOTE: You can also inspect other fields in document_details as needed
int timeout = 10;while (status != "completed" && status != "failed"){ await Task.Delay(1000); timeout--; if (timeout <= 0) { Console.WriteLine("Timeout reached"); break; } status = await GetStatus(client, baseUrl, documentId);}
static async Task<string> GetStatus(HttpClient client, string baseUrl, string documentId){ try { var response = await client.GetAsync($"{baseUrl}/documents/{documentId}/"); response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync(); var jsonDoc = JsonDocument.Parse(content); return jsonDoc.RootElement.GetProperty("status").GetString(); } catch (Exception e) { Console.WriteLine($"Error getting status: {e.Message}"); return "unknown"; }}import java.net.HttpURLConnection;import java.net.URL;import java.io.BufferedReader;import java.io.InputStreamReader;import java.net.CookieManager;import java.net.CookieHandler;import java.nio.charset.StandardCharsets;import org.json.JSONObject;
public class PollingStatusTracking { public static void main(String[] args) throws Exception { String baseUrl = "http://dev-api.v2.areal.ai/api/v2";
// 0. Login - details in Authentication section ... client is authenticated // 1. Starting the processing ... we get a request_id (which is actually the document_id) -- details in Processing section // 2. Polling the status of the processing
String documentId = "your_document_id_here";
CookieManager cookieManager = new CookieManager(); CookieHandler.setDefault(cookieManager);
String status = getStatus(baseUrl, documentId); // NOTE: You can also inspect other fields in document_details as needed
int timeout = 10; while (!status.equals("completed") && !status.equals("failed")) { Thread.sleep(1000); timeout--; if (timeout <= 0) { System.out.println("Timeout reached"); break; } status = getStatus(baseUrl, documentId); } }
private static String getStatus(String baseUrl, String documentId) { try { URL url = new URL(baseUrl + "/documents/" + documentId + "/"); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setDoInput(true);
int responseCode = connection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { BufferedReader reader = new BufferedReader( new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8) ); StringBuilder response = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { response.append(line); } reader.close();
JSONObject documentDetails = new JSONObject(response.toString()); return documentDetails.getString("status"); } connection.disconnect(); } catch (Exception e) { System.out.println("Error getting status: " + e.getMessage()); } return "unknown"; }}Meaning of the status
Section titled “Meaning of the status”- pending: We received your request, but processing has not started yet.
- preparing: The document is being classified.
- processing: Data extraction is in progress.
- completed: Processing is complete and successful.
- failed: Processing failed. Please check the error details or try again.
WebSocket API
Section titled “WebSocket API”Document Processing & DocPush
Section titled “Document Processing & DocPush”from pydantic import BaseModelfrom typing import Optional
# When an existing documents status changes (e.g processing -> completed)class FileStatusUpdatedMessage(BaseModel): class Meta(BaseModel): upload_session_id: str id: str user: User
class Data(BaseModel): status: Status
type: str = 'FILE_STATUS_UPDATED' meta: Meta data: Data
# When a new document is created (e.g. preparing can generate multiple documents)class FileCreatedMessage(BaseModel): class Meta(BaseModel): upload_session_id: str user: User
type: str = 'FILE_CREATED' meta: Meta data: SessionDetailListItem
# When a document is removedclass FileDeletedMessage(BaseModel): class Meta(BaseModel): upload_session_id: str id: str user: User
type: str = 'FILE_DELETED' meta: Meta data: None = None
class User(BaseModel): id: str
# When a document is pushed to a gateway (e.g. Encompass DocPush)class DocPushFinishedMessage(BaseModel): class Meta(BaseModel): upload_session_id: str id: str user: User
class Data(BaseModel): status: Status error: Optional[str]
type: str = 'DOC_PUSH_FINISHED' meta: Meta data: Datausing System;using System.Text.Json.Serialization;
// When an existing documents status changes (e.g processing -> completed)public class FileStatusUpdatedMessage{ public class Meta { [JsonPropertyName("upload_session_id")] public string UploadSessionId { get; set; }
[JsonPropertyName("id")] public string Id { get; set; }
[JsonPropertyName("user")] public User User { get; set; } }
public class Data { [JsonPropertyName("status")] public string Status { get; set; } }
[JsonPropertyName("type")] public string Type { get; set; } = "FILE_STATUS_UPDATED";
[JsonPropertyName("meta")] public Meta Meta { get; set; }
[JsonPropertyName("data")] public Data Data { get; set; }}
// When a new document is created (e.g. preparing can generate multiple documents)public class FileCreatedMessage{ public class Meta { [JsonPropertyName("upload_session_id")] public string UploadSessionId { get; set; }
[JsonPropertyName("user")] public User User { get; set; } }
[JsonPropertyName("type")] public string Type { get; set; } = "FILE_CREATED";
[JsonPropertyName("meta")] public Meta Meta { get; set; }
[JsonPropertyName("data")] public SessionDetailListItem Data { get; set; }}
// When a document is removedpublic class FileDeletedMessage{ public class Meta { [JsonPropertyName("upload_session_id")] public string UploadSessionId { get; set; }
[JsonPropertyName("id")] public string Id { get; set; }
[JsonPropertyName("user")] public User User { get; set; } }
[JsonPropertyName("type")] public string Type { get; set; } = "FILE_DELETED";
[JsonPropertyName("meta")] public Meta Meta { get; set; }
[JsonPropertyName("data")] public object Data { get; set; } = null;}
public class User{ [JsonPropertyName("id")] public string Id { get; set; }}
// When a document is pushed to a gateway (e.g. Encompass DocPush)public class DocPushFinishedMessage{ public class Meta { [JsonPropertyName("upload_session_id")] public string UploadSessionId { get; set; }
[JsonPropertyName("id")] public string Id { get; set; }
[JsonPropertyName("user")] public User User { get; set; } }
public class Data { [JsonPropertyName("status")] public string Status { get; set; }
[JsonPropertyName("error")] public string Error { get; set; } }
[JsonPropertyName("type")] public string Type { get; set; } = "DOC_PUSH_FINISHED";
[JsonPropertyName("meta")] public Meta Meta { get; set; }
[JsonPropertyName("data")] public Data Data { get; set; }}import org.json.JSONObject;
// When an existing documents status changes (e.g processing -> completed)class FileStatusUpdatedMessage { public static class Meta { public String upload_session_id; public String id; public User user; }
public static class Data { public String status; }
public String type = "FILE_STATUS_UPDATED"; public Meta meta; public Data data;}
// When a new document is created (e.g. preparing can generate multiple documents)class FileCreatedMessage { public static class Meta { public String upload_session_id; public User user; }
public String type = "FILE_CREATED"; public Meta meta; public SessionDetailListItem data;}
// When a document is removedclass FileDeletedMessage { public static class Meta { public String upload_session_id; public String id; public User user; }
public String type = "FILE_DELETED"; public Meta meta; public Object data = null;}
class User { public String id;}
// When a document is pushed to a gateway (e.g. Encompass DocPush)class DocPushFinishedMessage { public static class Meta { public String upload_session_id; public String id; public User user; }
public static class Data { public String status; public String error; }
public String type = "DOC_PUSH_FINISHED"; public Meta meta; public Data data;}CDBalancer & CDPush/Pull
Section titled “CDBalancer & CDPush/Pull”from pydantic import BaseModelfrom typing import Optional
# When the status of the CDBalancer changes (e.g. preparing -> processing -> completed)class CDBalancerStatusUpdatedMessage(BaseModel): class Meta(BaseModel): upload_session_id: str id: str user: User
class Data(BaseModel): status: Status
type: str = 'CDBALANCER_STATUS_UPDATED' meta: Meta data: Data
# When a CDPull is started (e.g. Encompass CDPull)class CDPullStartedMessage(BaseModel): class Meta(BaseModel): upload_session_id: str user: User
class Data(BaseModel): class Document(BaseModel): id: str name: str status: Status
document: Document
type: str = 'CD_PULL_STARTED' meta: Meta data: Data
# When a CDPull is finished (e.g. Encompass CDPull)class CDPullFinishedMessage(BaseModel): class Meta(BaseModel): upload_session_id: str user: User
class Data(BaseModel): class Document(BaseModel): id: str name: str status: Status
document: Optional[Document] status: Status error: Optional[str]
type: str = 'CD_PULL_FINISHED' meta: Meta data: Data
# When a CDPush is finished (e.g. Encompass CDPush)class CDPushFinishedMessage(BaseModel): class Meta(BaseModel): upload_session_id: str cdbalancer_request_id: str gateway_request_id: Optional[str] = None user: User
class Data(BaseModel): status: Status error: Optional[str] stats_message: Optional[str] = None stats_status: Optional[str] = None
type: str = 'CD_PUSH_FINISHED' meta: Meta data: Datausing System;using System.Text.Json.Serialization;
// When the status of the CDBalancer changes (e.g. preparing -> processing -> completed)public class CDBalancerStatusUpdatedMessage{ public class Meta { [JsonPropertyName("upload_session_id")] public string UploadSessionId { get; set; }
[JsonPropertyName("id")] public string Id { get; set; }
[JsonPropertyName("user")] public User User { get; set; } }
public class Data { [JsonPropertyName("status")] public string Status { get; set; } }
[JsonPropertyName("type")] public string Type { get; set; } = "CDBALANCER_STATUS_UPDATED";
[JsonPropertyName("meta")] public Meta Meta { get; set; }
[JsonPropertyName("data")] public Data Data { get; set; }}
// When a CDPull is started (e.g. Encompass CDPull)public class CDPullStartedMessage{ public class Meta { [JsonPropertyName("upload_session_id")] public string UploadSessionId { get; set; }
[JsonPropertyName("user")] public User User { get; set; } }
public class Data { public class Document { [JsonPropertyName("id")] public string Id { get; set; }
[JsonPropertyName("name")] public string Name { get; set; }
[JsonPropertyName("status")] public string Status { get; set; } }
[JsonPropertyName("document")] public Document Document { get; set; } }
[JsonPropertyName("type")] public string Type { get; set; } = "CD_PULL_STARTED";
[JsonPropertyName("meta")] public Meta Meta { get; set; }
[JsonPropertyName("data")] public Data Data { get; set; }}
// When a CDPull is finished (e.g. Encompass CDPull)public class CDPullFinishedMessage{ public class Meta { [JsonPropertyName("upload_session_id")] public string UploadSessionId { get; set; }
[JsonPropertyName("user")] public User User { get; set; } }
public class Data { public class Document { [JsonPropertyName("id")] public string Id { get; set; }
[JsonPropertyName("name")] public string Name { get; set; }
[JsonPropertyName("status")] public string Status { get; set; } }
[JsonPropertyName("document")] public Document Document { get; set; }
[JsonPropertyName("status")] public string Status { get; set; }
[JsonPropertyName("error")] public string Error { get; set; } }
[JsonPropertyName("type")] public string Type { get; set; } = "CD_PULL_FINISHED";
[JsonPropertyName("meta")] public Meta Meta { get; set; }
[JsonPropertyName("data")] public Data Data { get; set; }}
// When a CDPush is finished (e.g. Encompass CDPush)public class CDPushFinishedMessage{ public class Meta { [JsonPropertyName("upload_session_id")] public string UploadSessionId { get; set; }
[JsonPropertyName("cdbalancer_request_id")] public string CDBalancerRequestId { get; set; }
[JsonPropertyName("gateway_request_id")] public string GatewayRequestId { get; set; }
[JsonPropertyName("user")] public User User { get; set; } }
public class Data { [JsonPropertyName("status")] public string Status { get; set; }
[JsonPropertyName("error")] public string Error { get; set; }
[JsonPropertyName("stats_message")] public string StatsMessage { get; set; }
[JsonPropertyName("stats_status")] public string StatsStatus { get; set; } }
[JsonPropertyName("type")] public string Type { get; set; } = "CD_PUSH_FINISHED";
[JsonPropertyName("meta")] public Meta Meta { get; set; }
[JsonPropertyName("data")] public Data Data { get; set; }}// When the status of the CDBalancer changes (e.g. preparing -> processing -> completed)class CDBalancerStatusUpdatedMessage { public static class Meta { public String upload_session_id; public String id; public User user; }
public static class Data { public String status; }
public String type = "CDBALANCER_STATUS_UPDATED"; public Meta meta; public Data data;}
// When a CDPull is started (e.g. Encompass CDPull)class CDPullStartedMessage { public static class Meta { public String upload_session_id; public User user; }
public static class Data { public static class Document { public String id; public String name; public String status; }
public Document document; }
public String type = "CD_PULL_STARTED"; public Meta meta; public Data data;}
// When a CDPull is finished (e.g. Encompass CDPull)class CDPullFinishedMessage { public static class Meta { public String upload_session_id; public User user; }
public static class Data { public static class Document { public String id; public String name; public String status; }
public Document document; public String status; public String error; }
public String type = "CD_PULL_FINISHED"; public Meta meta; public Data data;}
// When a CDPush is finished (e.g. Encompass CDPush)class CDPushFinishedMessage { public static class Meta { public String upload_session_id; public String cdbalancer_request_id; public String gateway_request_id; public User user; }
public static class Data { public String status; public String error; public String stats_message; public String stats_status; }
public String type = "CD_PUSH_FINISHED"; public Meta meta; public Data data;}Copilot Tasks
Section titled “Copilot Tasks”from pydantic import BaseModelfrom typing import Optional
# When a copilot task is updated (e.g. pending -> processing -> completed)class CopilotTaskUpdatedMessage(BaseModel): class Meta(BaseModel): upload_session_id: str task_group_id: str
class Data(BaseModel): task: TaskSchema error: Optional[str]
type: str = 'COPILOT_TASK_UPDATED' meta: Meta data: Data
# When a new copilot task is createdclass CopilotTaskCreatedMessage(BaseModel): class Meta(BaseModel): upload_session_id: str task_group_id: str
class Data(BaseModel): task: TaskSchema
type: str = 'COPILOT_TASK_CREATED' meta: Meta data: Data
# When a copilot task is deletedclass CopilotTaskDeletedMessage(BaseModel): class Meta(BaseModel): task_group_id: str
class Data(BaseModel): task_id: str
type: str = 'COPILOT_TASK_DELETED' meta: Meta data: Datausing System;using System.Text.Json.Serialization;
// When a copilot task is updated (e.g. pending -> processing -> completed)public class CopilotTaskUpdatedMessage{ public class Meta { [JsonPropertyName("upload_session_id")] public string UploadSessionId { get; set; }
[JsonPropertyName("task_group_id")] public string TaskGroupId { get; set; } }
public class Data { [JsonPropertyName("task")] public TaskSchema Task { get; set; }
[JsonPropertyName("error")] public string Error { get; set; } }
[JsonPropertyName("type")] public string Type { get; set; } = "COPILOT_TASK_UPDATED";
[JsonPropertyName("meta")] public Meta Meta { get; set; }
[JsonPropertyName("data")] public Data Data { get; set; }}
// When a new copilot task is createdpublic class CopilotTaskCreatedMessage{ public class Meta { [JsonPropertyName("upload_session_id")] public string UploadSessionId { get; set; }
[JsonPropertyName("task_group_id")] public string TaskGroupId { get; set; } }
public class Data { [JsonPropertyName("task")] public TaskSchema Task { get; set; } }
[JsonPropertyName("type")] public string Type { get; set; } = "COPILOT_TASK_CREATED";
[JsonPropertyName("meta")] public Meta Meta { get; set; }
[JsonPropertyName("data")] public Data Data { get; set; }}
// When a copilot task is deletedpublic class CopilotTaskDeletedMessage{ public class Meta { [JsonPropertyName("task_group_id")] public string TaskGroupId { get; set; } }
public class Data { [JsonPropertyName("task_id")] public string TaskId { get; set; } }
[JsonPropertyName("type")] public string Type { get; set; } = "COPILOT_TASK_DELETED";
[JsonPropertyName("meta")] public Meta Meta { get; set; }
[JsonPropertyName("data")] public Data Data { get; set; }}// When a copilot task is updated (e.g. pending -> processing -> completed)class CopilotTaskUpdatedMessage { public static class Meta { public String upload_session_id; public String task_group_id; }
public static class Data { public TaskSchema task; public String error; }
public String type = "COPILOT_TASK_UPDATED"; public Meta meta; public Data data;}
// When a new copilot task is createdclass CopilotTaskCreatedMessage { public static class Meta { public String upload_session_id; public String task_group_id; }
public static class Data { public TaskSchema task; }
public String type = "COPILOT_TASK_CREATED"; public Meta meta; public Data data;}
// When a copilot task is deletedclass CopilotTaskDeletedMessage { public static class Meta { public String task_group_id; }
public static class Data { public String task_id; }
public String type = "COPILOT_TASK_DELETED"; public Meta meta; public Data data;}