API Reference
Harness API — v1 · Base:https://harness.jheel.io
The jheel.io Harness API provides an asynchronous document extraction workflow, enterprise RAG queries, and autonomous agent management — all through a consistent, authenticated REST interface.
Workflow overview
For document extraction, the standard flow is:
- Create a session — a container for one or more related documents.
- Upload one or more documents to the session.
-
Poll the document endpoint until
ExtractedJSONis populated orErrorMessageis non-empty.
ExtractedJSON will be available immediately after
upload.
Authentication
All endpoints require an API key passed in the request header.
API keys are
issued per account and are prefixed jh_live_ for
production.
Header
X-API-KEY: jh_live_your_key_here
Include this header on every request. Requests without a valid
key return
401 Unauthorized.
Base URL
https://harness.jheel.io
All endpoints are relative to this base URL. HTTPS only.
Create Session
Creates a new extraction session. A session acts as a container
for one or more
uploaded documents. Use a meaningful ClientID for
your own reference —
it is returned in all session responses.
Headers
Accept: application/json Content-Type: application/json X-API-KEY: <api-key>
Request body
| Field | Type | Required | Description |
|---|---|---|---|
ClientID |
string | required | Your own reference identifier for this session. Returned as-is in all responses. |
Example request
POST https://harness.jheel.io/harness/session/
Content-Type: application/json
X-API-KEY: jh_live_xk29fmz...
{
"ClientID": "q4-audit-2026"
}
Example response
{
"ID": 198,
"TenantID": 1,
"ClientID": "q4-audit-2026",
"Data": null,
"Created": "2026-06-14T09:00:00.000000Z",
"Updated": "2026-06-14T09:00:00.000000Z"
}
Python
import requests
BASE_URL = "https://harness.jheel.io"
API_KEY = "jh_live_xk29fmz..."
response = requests.post(
f"{BASE_URL}/harness/session/",
headers={
"Accept": "application/json",
"Content-Type": "application/json",
"X-API-KEY": API_KEY,
},
json={"ClientID": "q4-audit-2026"},
)
response.raise_for_status()
session = response.json()
session_id = session["ID"] # → 198
Upload Document
Uploads a document to an existing session. A session can hold multiple documents. Extraction begins immediately after upload and runs asynchronously — poll the status endpoint to retrieve results.
Headers
Accept: application/json X-API-KEY: <api-key>
Content-Type manually when using
multipart form uploads
in Python's requests library. Let the library set
it — it includes
the required boundary parameter automatically.
Multipart form fields
| Field | Type | Required | Description |
|---|---|---|---|
type |
string | required | Document type. Accepted values: invoice,
bank-statement,
healthcare-form,
brokerage-report,
purchase-order |
file |
file | required | The document file. Accepted formats: PDF, DOCX, XLSX, JPG, PNG. |
Example request
POST https://harness.jheel.io/harness/session/198/document/ Accept: application/json X-API-KEY: jh_live_xk29fmz... Content-Type: multipart/form-data; boundary=... type=invoice file=@q4-invoice.pdf
Example response
{
"ID": 47,
"SessionID": 198,
"Document": "invoice__47.pdf",
"DocumentName": "q4-invoice.pdf",
"MimeType": "application/pdf",
"ExtractedJSON": null,
"CostUSD": 0,
"ErrorMessage": "",
"ResponseTimeMs": 0,
"Type": "invoice",
"Created": "2026-06-14T09:01:00.000000Z",
"Updated": "2026-06-14T09:01:00.000000Z"
}
Python
import requests
session_id = 198
response = requests.post(
f"{BASE_URL}/harness/session/{session_id}/document/",
headers={
"Accept": "application/json",
"X-API-KEY": API_KEY,
},
data={"type": "invoice"},
files={"file": ("q4-invoice.pdf", open("q4-invoice.pdf", "rb"), "application/pdf")},
)
response.raise_for_status()
document = response.json()
document_id = document["ID"] # → 47
Get Document Status
Retrieves the current processing status and extracted JSON for a document. Use this endpoint to poll until extraction completes.
Headers
Accept: application/json X-API-KEY: <api-key>
Example request
GET https://harness.jheel.io/harness/session/198/document/47/ Accept: application/json X-API-KEY: jh_live_xk29fmz...
Example response — extraction complete
{
"ID": 47,
"SessionID": 198,
"Document": "invoice__47.pdf",
"DocumentName": "q4-invoice.pdf",
"MimeType": "application/pdf",
"CostUSD": 0.002346,
"ResponseTimeMs": 5302,
"Type": "invoice",
"ErrorMessage": "",
"ExtractedJSON": {
"VendorName": "Meridian Supply Co.",
"InvoiceDate": "2026-06-01",
"DueDate": "2026-07-01",
"InvoiceNumber": "INV-4892",
"TotalAmount": 12450.00,
"TaxAmount": 1128.00,
"LineItems": [
{
"Description": "Professional Services — Q2",
"Quantity": 1,
"UnitPrice": 12450.00,
"Total": 12450.00
}
]
},
"Created": "2026-06-14T09:01:00.000000Z",
"Updated": "2026-06-14T09:01:05.000000Z"
}
Response fields
| Field | Type | Description |
|---|---|---|
ExtractedJSON |
object | null | Null while processing. Populated with structured data on completion. |
ErrorMessage |
string | Empty string on success. Contains failure detail if extraction failed. |
CostUSD |
float | Processing cost for this document in USD. |
ResponseTimeMs |
integer | Extraction processing time in milliseconds. |
Polling Guide
Extraction is asynchronous. After uploading a document, poll the status endpoint at a regular interval until one of two terminal states is reached.
States
| State | ExtractedJSON | ErrorMessage |
|---|---|---|
| Processing | null |
"" (empty) |
| Complete | object | "" (empty) |
| Failed | null |
non-empty string |
Python polling example
import time
import requests
def poll_document(session_id: int, document_id: int) -> dict:
url = f"{BASE_URL}/harness/session/{session_id}/document/{document_id}/"
headers = {"Accept": "application/json", "X-API-KEY": API_KEY}
while True:
resp = requests.get(url, headers=headers)
resp.raise_for_status()
data = resp.json()
if data.get("ErrorMessage"):
raise RuntimeError(f"Extraction failed: {data['ErrorMessage']}")
if data.get("ExtractedJSON") is not None:
return data["ExtractedJSON"]
print("Processing… retrying in 2s")
time.sleep(2)
result = poll_document(session_id=198, document_id=47)
print(result["VendorName"]) # → "Meridian Supply Co."
RAG — Query Documents
Once documents have been processed within a session, they form a queryable knowledge base. Submit a natural-language question against the session's document corpus and receive a structured answer with source attribution and confidence scoring.
Headers
Accept: application/json Content-Type: application/json X-API-KEY: <api-key>
Request body
| Field | Type | Required | Description |
|---|---|---|---|
session_id |
integer | required | The session ID whose documents will be queried. |
q |
string | required | Natural-language question. Query in plain English — no special syntax required. |
Example request
POST https://harness.jheel.io/harness/rag/query/
Content-Type: application/json
X-API-KEY: jh_live_xk29fmz...
{
"session_id": 198,
"q": "What are the payment terms across all Q4 vendor invoices?"
}
Example response
{
"answer": "Net-30 is specified in 11 of 14 vendor invoices reviewed. Three vendors — Meridian Supply Co. (INV-4892), Apex Materials (INV-4834), and Bright Tech (INV-4821) — specify Net-45 terms.",
"sources": [
{
"document_id": 47,
"document": "meridian-inv.pdf",
"page": 1
},
{
"document_id": 51,
"document": "apex-inv.pdf",
"page": 1
}
],
"confidence": 0.94,
"session_id": 198
}
Response fields
| Field | Type | Description |
|---|---|---|
answer |
string | Natural-language answer synthesised from the document corpus. |
sources |
array | The specific documents and pages that informed the answer. |
confidence |
float (0–1) | Model confidence in the answer. Values below 0.7 indicate low certainty. |
Agents — Create Agent
Creates and activates an autonomous agent. Provide a plain-English prompt describing the agent's goal, and declare the tools it is permitted to call. The agent begins listening for its trigger event immediately on creation.
Headers
Accept: application/json Content-Type: application/json X-API-KEY: <api-key>
Request body
| Field | Type | Required | Description |
|---|---|---|---|
prompt |
string | required | Plain-English description of the agent's goal and trigger condition. Be specific about the trigger and the desired outcome. |
tools |
string[] | required | Array of tool identifiers the agent may call. See Available Tools. Principle of least privilege — only include tools required for the task. |
Example request
POST https://harness.jheel.io/harness/agent/
Content-Type: application/json
X-API-KEY: jh_live_xk29fmz...
{
"prompt": "When a new Shopify order is placed, check available stock for each line item in NetSuite. If all items are in stock, confirm the order and send a receipt email via Postmark. If any item is out of stock, flag the order for review.",
"tools": [
"shopify.orders.read",
"shopify.orders.update",
"netsuite.items.read",
"postmark.email.send"
]
}
Example response
{
"agent_id": "agt_7xk2mfp",
"status": "active",
"trigger": "shopify.order.created",
"tools_authorized": [
"shopify.orders.read",
"shopify.orders.update",
"netsuite.items.read",
"postmark.email.send"
],
"created": "2026-06-14T09:12:00.000000Z",
"updated": "2026-06-14T09:12:00.000000Z"
}
Agents — Get Agent
Retrieves the current status of an agent, including its configuration and execution history. Use this to monitor what the agent has done and inspect its most recent actions.
Example request
GET https://harness.jheel.io/harness/agent/agt_7xk2mfp/ Accept: application/json X-API-KEY: jh_live_xk29fmz...
Example response
{
"agent_id": "agt_7xk2mfp",
"status": "active",
"trigger": "shopify.order.created",
"tools_authorized": [
"shopify.orders.read",
"shopify.orders.update",
"netsuite.items.read",
"postmark.email.send"
],
"executions": [
{
"execution_id": "exe_4ab3c1",
"triggered_at": "2026-06-14T11:30:00Z",
"status": "completed",
"actions": [
{ "tool": "shopify.orders.read", "result": "success" },
{ "tool": "netsuite.items.read", "result": "success" },
{ "tool": "shopify.orders.update","result": "success" },
{ "tool": "postmark.email.send", "result": "success" }
]
}
],
"created": "2026-06-14T09:12:00.000000Z"
}
Agents — Edit Agent
Updates an existing agent's prompt and/or tool list. The agent must be deactivated before editing, then reactivated. A full prompt and tools array is expected — partial updates are not supported.
Example request
PUT https://harness.jheel.io/harness/agent/agt_7xk2mfp/edit/
Content-Type: application/json
X-API-KEY: jh_live_xk29fmz...
{
"prompt": "When a new Shopify order is placed, check stock in NetSuite. If in stock, post a journal entry to NetSuite, confirm the Shopify order, and send a receipt via Postmark.",
"tools": [
"shopify.orders.read",
"shopify.orders.update",
"netsuite.items.read",
"netsuite.journal_entries.create",
"postmark.email.send"
]
}
Example response
{
"agent_id": "agt_7xk2mfp",
"status": "active",
"tools_authorized": [
"shopify.orders.read",
"shopify.orders.update",
"netsuite.items.read",
"netsuite.journal_entries.create",
"postmark.email.send"
],
"updated": "2026-06-14T14:00:00.000000Z"
}
Agents — Deactivate Agent
Deactivates a running agent. The agent stops listening for trigger events and will not execute. Any in-progress executions at the time of deactivation are allowed to complete.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
agent_id |
string | required | The ID of the agent to deactivate. |
Example request
POST https://harness.jheel.io/harness/agent/deactivate/
Content-Type: application/json
X-API-KEY: jh_live_xk29fmz...
{
"agent_id": "agt_7xk2mfp"
}
Example response
{
"agent_id": "agt_7xk2mfp",
"status": "deactivated",
"updated": "2026-06-14T14:30:00.000000Z"
}
Available Tools
The following tools can be declared in an agent's
tools array.
Only include tools the agent requires — undeclared tools cannot
be called
at runtime, even if the prompt references them.
NetSuite
Shopify
Postmark
Recommended Client Flow
Complete document extraction in three requests:
Step 1 — Create session
POST /harness/session/
→ { "ID": 198 }
Step 2 — Upload one or more documents
POST /harness/session/198/document/
→ { "ID": 47, "ExtractedJSON": null } # processing begins
Multiple documents can belong to the same session:
POST /harness/session/198/document/
→ { "ID": 48, "ExtractedJSON": null }
POST /harness/session/198/document/
→ { "ID": 49, "ExtractedJSON": null }
Step 3 — Poll for results
GET /harness/session/198/document/47/ # Continue until: # ExtractedJSON != null → success # ErrorMessage != "" → failure
Response Fields
Common fields returned across document endpoints:
| Field | Type | Description |
|---|---|---|
ID |
integer | Unique identifier for the resource. |
SessionID |
integer | The session this document belongs to. |
ExtractedJSON |
object | null | Structured extraction result. Null while processing. |
ErrorMessage |
string | Empty on success; descriptive failure reason on error. |
CostUSD |
float | Processing cost for this document in USD. |
ResponseTimeMs |
integer | Total extraction processing time in milliseconds. |
AdditionalFields |
object | Model-specific metadata with confidence scores and evidence references. |
Notes
- Document processing is asynchronous. Always poll — never assume immediate results.
- Multiple documents can belong to a single session. Process related documents together for better RAG query context.
-
CostUSDreflects the per-document processing cost, billed to your account. -
AdditionalFieldscontains model-specific extracted metadata with confidence scores — the schema varies by document type. -
Agents operate on principle of least privilege — only tools
explicitly declared in the
toolsarray can be called at runtime. - All timestamps are UTC ISO 8601.
Questions or integration issues? Get in touch →