DocuLink Studio — Python SDK
Official Python SDK for the DocuLink Studio Customer API — OCR / LLM document processing and AI chat. It implements the full 59-endpoint contract with automatic authentication, token auto-refresh, typed models, multipart uploads, SSE chat streaming and Socket.IO real-time subscriptions.
- Distribution name (PyPI):
doculink-studio - Import package:
doculink_studio
🆕 What's new in 0.5.0
All-Process — hand the whole pipeline to the server instead of driving it yourself: pass
AutoProcessto the upload, or callallProcesson an uploaded scan (it also resumes one stuck at"3"/"5"). Then pollgetScanStatusuntilDone.scanDocumentnow defaults to this path and uses no Socket.IO at all, so it works from serverless functions, batch jobs and anything behind a firewall — passallProcess: falsefor the 0.4.x socket-driven behaviour.Human-in-the-loop —
editOutputRecord/deleteOutputRecordchange one row instead of PUT-ing the whole document back;getMappingCandidates/getMappingMeta/pickMappingCandidatelet you inspect and change the master-data row the engine matched;exportScanOutputandgetScanSourceFiledownload the result and the original file.listScansfinally lists scans across every task.⚠️
updateJsonOutputsaves a full-output override thatgetJsonOutputreturns in preference to the server's own output, so while one exists the record editors answer 409 on purpose — callclearOutputOverridefirst.Row-level master data — read/add/update/delete individual rows plus search, history and a validating import, instead of replacing the whole table.
Account self-service —
getMe, usage reports + export, invoices,rotateApiKeyandgetAuditLogs. Rotating the key revokes every previously issued token immediately; the SDK swaps in the new key and re-authenticates for you, but the key is shown only once — persist it.
⚠️ Breaking change in v0.4.0 — document status constants.
DocumentStatusnow matches the server's full lifecycle:"1"Uploaded,"2"/"3"OCR (processing/completed),"4"/"5"Schema,"6"/"7"Mapping,"9"Error —DocumentStatus.COMPLETEDchanged value from"4"to"7", andOCR/SCHEMA/MAPPINGwere replaced by*_PROCESSING/*_COMPLETED. See../CHANGELOG.md.
⚠️ Also in v0.4.0: the doc-scan
joinpayload is now the object{"room": ...}the server actually reads (bare-string joins were silently ignored — realtime never worked through 0.2.0–0.3.1);return_format_typetakes"1"/"2"(the return format'sType), not"JSON"|"XML"|"CSV"; newscan_documentpipeline helper +update_json_output.
⚠️ Breaking change in v0.3.0 — real-time subscription.
subscribe_document_scan(...)now takes the TaskUUID (the task id you use for upload), not the DocumentScanUUID. The server broadcastsdoc-scanevents to roomdoc-scan-{TaskUUID}; a0.2.0subscription that passed the scan UUID received nothing. Each event's payloadUUIDis still the DocumentScanUUID. See../CHANGELOG.md.
ℹ️ v0.3.1 —
subscribe_document_scan(...)now sends theauth={"token": ...}Socket.IO handshake the document server requires. Authenticate (or pre-set an access token) before subscribing; anonymous connects are rejected.
Install
pip install doculink-studio
Requires Python 3.9+. Runtime dependencies: requests, pydantic>=2,
python-socketio[client], websocket-client.
Quickstart
from doculink_studio import DoculinkClient
client = DoculinkClient(
provider_api_key="<35-char provider key>",
customer_api_key="<35-char customer key>",
email="you@example.com", # optional
# base_url / document_ws_url / chat_ws_url default to the test environment
)
# Authentication is automatic on the first call, but you can force it:
client.authenticate()
usage = client.get_usage()
print(usage.planCode, usage.quotaRemaining)
Configuration
| Kwarg | Default |
|---|---|
base_url |
https://test-api-provider.doculink.studio/api/v1 |
document_ws_url |
https://test-ws.doculink.studio |
chat_ws_url |
https://test-ws.doculink.studio:8000 |
timeout |
30 (SSE / sync chat use 300s automatically) |
access_token / refresh_token |
optional — pre-set to skip initial auth |
session |
optional requests.Session |
Authentication & auto-refresh
The client stores the access + refresh tokens after authenticate(). Every
authenticated request sets Authorization: Bearer <AccessToken>. On a 401 the
client transparently refreshes the access token and retries once; if refresh
fails it re-authenticates with the API keys and retries once.
Upload & process a document
import uuid
task_id = str(uuid.uuid4()) # TaskId = UUID v4 ที่คุณสร้างเอง (**v4 เท่านั้น** — เวอร์ชันอื่นถูกปฏิเสธ)
result = client.upload_file(
task_id=task_id,
file=b"...pdf bytes...", # bytes, a file path (str), or a stream
schema_uuid="<schema uuid>",
return_format_uuid="<return format uuid>",
return_format_type="1", # "1" public / "2" customer — ค่า Type ของ return format ที่เลือก (ไม่ใช่ชื่อ file format)
client_uuid=None, # optional
filename="invoice.pdf",
)
doc_id = result.DocumentScanUUID
# Drive the pipeline (async — wait for each stage's odd status via realtime):
client.ocr_process(task_id, doc_id) # → wait for status "3"
client.schema_process(task_id, doc_id) # → wait for status "5"
client.mapping_process(task_id, doc_id) # → wait for status "7"
output = client.get_json_output(task_id, doc_id)
print(output.JsonOutput)
Document status constants (even = stage running, odd = stage done — wait for
the odd status of each stage before calling the next endpoint):
DocumentStatus.UPLOADED ("1"), OCR_PROCESSING ("2"), OCR_COMPLETED
("3"), SCHEMA_PROCESSING ("4"), SCHEMA_COMPLETED ("5"),
MAPPING_PROCESSING ("6"), COMPLETED ("7"), ERROR ("9").
Real-time — document processing (Socket.IO)
from doculink_studio import DocScanUpdate
def on_update(u: DocScanUpdate):
print(u.Status, u.CurrentLog)
sub = client.subscribe_document_scan(task_id, on_update=on_update)
# ... later ...
sub.close()
The subscribe id is the TaskUUID — the same :taskid you upload to — not
the DocumentScanUUID. On connect the SDK emits join with the object
{"room": "doc-scan-<TaskUUID>"} (same shape as chat) and dispatches
doc-scan events parsed into DocScanUpdate. A task may hold several scans
through the one room; each update's UUID field is the DocumentScanUUID it
belongs to.
Chat
session_id = client.create_chat_session(model="gpt-4o-mini")
# Synchronous (blocks until the full reply is ready):
msg = client.send_chat_message_sync(session_id, "Summarise the invoice")
print(msg.Content)
# Streaming over SSE:
def on_event(evt):
if "chunk" in evt:
print(evt["chunk"], end="")
final = client.send_chat_message_stream(session_id, "Explain more", on_event)
print("\nFinal:", final.Content)
# History / listing
sessions = client.list_chat_sessions(status="active", page=1, limit=20)
history = client.get_chat_history(session_id, page=1, limit=50)
Chat over Socket.IO
sub = client.subscribe_chat_session(
session_id,
on_start=lambda e: print("start", e),
on_chunk=lambda chunk, e: print(chunk, end=""),
on_end=lambda msg, e: print("\ndone:", msg.Content),
on_error=lambda e: print("error", e),
)
client.send_chat_message(session_id, "hello") # async (202) — watch the socket
# ...
sub.close()
The chat join emits an object {"room": "chat-<sessionID>"} and passes
auth={"token": <access token>} on the handshake when a token is available.
Error handling
Every method raises ApiError on HTTP >= 400 or an envelope status: false
(including billing rejections that come back as HTTP 200 with status: false).
from doculink_studio import ApiError
try:
client.get_usage()
except ApiError as e:
print(e.status_code, e.message, e.status)
Development
python -m venv .venv
source .venv/Scripts/activate # Git Bash on Windows
pip install -e ".[dev]"
pytest -q
More docs
See the bundled API reference at ../docs/index.html and
the canonical contract in ../CONTRACT.md.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file doculink_studio-0.5.0.tar.gz.
File metadata
- Download URL: doculink_studio-0.5.0.tar.gz
- Upload date:
- Size: 37.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cc81a7989a0681a64c88d528142c4e7c3303c0f56dea15442078c581cfab9fa8
|
|
| MD5 |
ae734de8d1bf3bd4b2d5fc4dd8dec01a
|
|
| BLAKE2b-256 |
a95a9ddac6674dbd791a5ded752a595c7c3a596789b0f2f7ab94ee0e2b9c9c30
|
File details
Details for the file doculink_studio-0.5.0-py3-none-any.whl.
File metadata
- Download URL: doculink_studio-0.5.0-py3-none-any.whl
- Upload date:
- Size: 27.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c06544e19d7bd0e4c1221d72053c6e1208c9f720ce47b9a8eb5c42a953beb8a7
|
|
| MD5 |
2c18d3600f04af5c2848bb157272d7af
|
|
| BLAKE2b-256 |
3da71b33ef0300c024d5da72cb5c274e96ca0a0d22fafc39e6b01141d35711f5
|