Partner API Reference
Introduction
Document Gateway is the clearinghouse for an organization’s documents of record, it ingests, certifies, vaults, classifies, abstracts, and tracks compliance for every document across its lifecycle. The Partner API is the programmatic surface over that whole platform: an external system, your DMS, data warehouse, ERP/CLM, analytics platform, or workflow engine, can push documents in, read any part of the record (documents & storage, vault & certificates, compliance, connectors & hierarchy, audit trail, plus AI abstracts), search, and react to events in real time.
The API spans the platform’s domains, not a single feature:
| Domain | What a partner does | Endpoints |
|---|---|---|
| Documents & storage | Push documents in; mirror the document register, storage tiers & dedup | POST /v1/documents, GET /v1/documents, /v1/storage-objects, /v1/duplicates, /v1/assets |
| Vault & certification | Read certified documents of record, certificates & Sentry fingerprints | /v1/vault, /v1/certificates |
| Compliance | Pull the requirement matrix & current compliance state | /v1/compliance-requirements, /v1/compliance-status |
| Connectors & hierarchy | Read connector health & the org taxonomy/nodes | /v1/connectors, /v1/hierarchy-nodes, /v1/counterparties |
| Access & audit | Stream the access log & lifecycle audit trail | /v1/access-logs, /v1/audit-logs |
| Reports | Pull submitted rent rolls/arrears/TB reports, their line items, QC, and correlation to the document set | /v1/reports, /v1/report-line-items, /v1/report-qc-results, /v1/report-correlation-results, /v1/report-exceptions |
| Document intelligence (Abstract.DI) | Mirror AI abstracts & families, retrieve sources, search | /v1/abstracts, /v1/families, POST /v1/search |
| Events | React when documents are ingested or abstracted | document.ingested, abstract.completed webhooks |
Document intelligence (abstracts/families) is one domain among several, see Platform resources for the full catalog and Ingest to push documents in.
Quickstart
1 · Get an API key
In Gateway, open API Marketplace → API Keys and issue a partner key. The key is
shown once, store it securely. Keys are prefixed aidi_
and carry a set of scopes.
2 · Make your first call
# Confirm your key and see its scopes
curl https://nwbjvdemkdvlzdbntgnd.supabase.co/functions/v1/partner-api/v1/me \
-H "Authorization: Bearer aidi_YOUR_KEY"
// 200 OK
{ "key_id": "8853f747-…", "scopes": ["abstracts:read","families:read","documents:read","search","webhooks:manage"], "tenant_id": null }
3 · Pull your abstracts
curl ".../partner-api/v1/abstracts?limit=10" \
-H "Authorization: Bearer aidi_YOUR_KEY"
Data-science quickstart
The thing that sets AI.DI apart: you don’t just get documents and metadata, you get the real intelligence. AI.DI abstracts any document, in any industry, contracts, agreements, invoices, policies, regulatory filings, certificates, HR records, financial statements, into structured fields, key clauses, and risk flags. In about a minute you can pull that intelligence into a pandas DataFrame and build on it.
From zero to a DataFrame of document intelligence
# pip install requests pandas
import requests, pandas as pd
API = "https://nwbjvdemkdvlzdbntgnd.supabase.co/functions/v1/partner-api"
H = {"Authorization": "Bearer aidi_YOUR_KEY"}
# 1 · Pull abstracts, each includes the FULL extracted intelligence
# (field_groups, key_clauses, risk_flags), not just metadata.
abstracts = requests.get(f"{API}/v1/abstracts?limit=100", headers=H).json()["data"]
# 2 · Flatten every extracted field into a tidy DataFrame
rows = [
{"document": a["file_name"], "doc_type": a.get("document_type"),
"group": g.get("group"), "field": f.get("label"), "value": f.get("value")}
for a in abstracts
for g in a.get("field_groups", [])
for f in g.get("fields", [])
]
df = pd.DataFrame(rows)
# 3 · Now query the intelligence like any dataset, pivot, filter, join, model
expirations = df[df.field.str.contains("Expiration", case=False, na=False)]
print(expirations.head())
# → any extracted field, across every document type, in one frame.
Ask in natural language (semantic search)
# Meaning-based search across the abstract intelligence, any document type
hits = requests.post(f"{API}/v1/search", headers=H, json={
"query": "agreements with an auto-renewal clause expiring in 2026",
"limit": 20,
}).json()["data"]
Connect an AI agent (MCP)
AI.DI ships a built-in MCP server, the same intelligence exposed as 25 tools any AI agent can call. Connect any MCP-capable AI assistant (one-click OAuth 2.1 connector, command line agents, or desktop assistants), a chat assistant that supports custom actions, or your own Python agent, all from one workspace-scoped key. Read-only and scoped to your own dedicated environment.
The toolset spans the whole platform, get_abstract (a document’s full
field groups, clauses & risk flags), query_abstract_fields (query extracted
fields across every document at once), get_family_abstract (rolled-up terms for a
grouped document set, e.g. a contract and its amendments), plus search, compliance, obligations,
anomalies, expirations, tower-agreement terms (get_tower_agreements), and, where a
Filestar DMS is connected, filestar_list_folders, filestar_list_documents,
filestar_search and filestar_get_document. Mint an agent key in Warehouse → API Keys (the Connect tab),
then point any MCP client at:
https://nwbjvdemkdvlzdbntgnd.supabase.co/functions/v1/mcp-server/mcp # Bearer dg_agent_YOUR_KEY
# Command line agents, one command:
your-client mcp add aidi --transport http .../functions/v1/mcp-server/mcp \
--header "Authorization: Bearer dg_agent_YOUR_KEY"
What you can build on top of AI.DI
Because the structured intelligence is an API, your data scientists and product teams can build directly on it, no document parsing, no model to train:
- Analytics & dashboards, turn any document set into a queryable dataset: registries, obligation calendars, expirations, exposure, KPIs, straight from the abstract fields.
- Models & pipelines, feed clean document features into your own models in your data platform or a notebook, no parsing, no model to train.
- Agents & assistants, attach any AI assistant and let it answer in natural language over your real documents, grounded and cited.
- Apps & extensions, embed AI.DI intelligence into the systems your clients already run (ERP, CLM, ICM, BI), or ship a new product on top of it.
- Event-driven workflows, subscribe to
abstract.completedwebhooks and trigger downstream automation the moment a document is understood.
Authentication & scopes
Every request (except the public OpenAPI spec) must include a bearer token:
Authorization: Bearer aidi_YOUR_KEY
Keys are stored only as SHA-256 hashes, Gateway cannot recover a lost key; rotate it
(revoke + reissue) instead. Each key is authorized for a set of scopes; a call
to an endpoint outside the key's scopes returns 403 forbidden.
| Scope | Grants |
|---|---|
abstracts:read | List and read abstracts |
families:read | List and read document families |
documents:read | Documents, storage objects, dedup candidates, assets + signed source-file URLs |
search | Run semantic / hybrid search |
vault:read | Vault records & document certificates |
compliance:read | Compliance requirements & status |
connectors:read | Integration connectors (no credentials), hierarchy & counterparties |
audit:read | Document access & audit logs |
reports:read | Submitted reports (rent rolls, arrears, trial balances) & their line items, QC results, correlation matches, and exceptions |
documents:write | Write: ingest documents into Gateway. Grant deliberately. |
webhooks:manage | Register, list, and delete webhooks |
Key & secret lifecycle
There are two secrets in an integration: your API key (you send it on every request) and a webhook signing secret (we use it to sign callbacks to you). Both are shown exactly once and stored only as hashes/secrets on our side, treat them like passwords.
| Stage | API key (aidi_…) | Webhook secret (whsec_…) |
|---|---|---|
| Issued by | Your imkore administrator, in Gateway → API Marketplace → API Keys. There is no self-service signup, request a key from your contact. | Automatically, when you register an endpoint via POST /v1/webhooks. |
| Shown | Once, at creation. ~192 bits of entropy. | Once, in the POST /v1/webhooks response (secret field). |
| Stored by imkore | Only a SHA-256 hash + a short display prefix. The full key is never recoverable. | The secret, used only to compute delivery signatures. |
| Carries | A fixed set of scopes and an optional tenant binding, both chosen at issue time. | The set of events you subscribed to. |
| Used for | Authorization: Bearer aidi_… on every request. | Verifying the X-Imkore-Signature on each webhook, see Verifying signatures. |
| Rotated | Revoke + reissue (there is no “reset”, the old key can’t be recovered). Old key returns 401 immediately on revoke. | Delete the webhook and register a new one to get a fresh secret. |
| Scope change | Issue a new key with the new scopes/tenant and retire the old one. Scopes are fixed per key. | Re-register with the new event list. |
documents:write. If a key is exposed, ask your administrator to revoke it;
revocation is instant.Base URL & format
https://nwbjvdemkdvlzdbntgnd.supabase.co/functions/v1/partner-api
All responses are JSON. Successful resource responses wrap the payload in
data; list responses add a pagination
object. All timestamps are ISO-8601 UTC.
Pagination
List endpoints accept limit (1 to 100, default 25) and
offset (default 0), and return a pagination
block with the exact total count.
{
"data": [ … ],
"pagination": { "limit": 25, "offset": 0, "total": 74, "returned": 25 }
}
For incremental sync, page by created_at using the
since filter on /v1/abstracts rather than
deep offsets.
Errors
Errors use standard HTTP status codes and a consistent envelope:
{ "error": { "code": "forbidden", "message": "Key lacks scope abstracts:read" } }
| Status | code | Meaning |
|---|---|---|
| 400 | bad_request | Missing/invalid parameter |
| 401 | unauthorized | Missing, malformed, or revoked key |
| 403 | forbidden | Key lacks the required scope |
| 404 | not_found | Resource does not exist (or not in your environment) |
| 500 | internal | Server error, safe to retry with backoff |
Rate limits & versioning
Be a good citizen: keep sustained traffic under ~120 requests/minute per key and
use the since cursor instead of polling full lists. The API is
versioned in the path (/v1/…); breaking changes ship under a new
version and the previous version remains available during a deprecation window announced
in the changelog.
Identity
Returns the calling key's id, scopes, and tenant binding. Use it to validate a key and discover what it can do.
Abstracts
An abstract is the structured extraction of a single document: typed fields grouped by section, risk flags, key clauses, a summary, and provenance. See the Abstract object reference.
abstracts:readList abstracts, newest first.
| Query param | Type | Notes |
|---|---|---|
limit | integer | 1 to 100, default 25 |
offset | integer | default 0 |
document_type | string | Exact match, e.g. Absolute Net Lease Agreement |
document_category | string | Exact match |
family_key | string | Only abstracts in this family |
since | ISO-8601 | Only abstracts created at/after this time |
curl ".../v1/abstracts?document_type=Absolute%20Net%20Lease%20Agreement&limit=2" \
-H "Authorization: Bearer aidi_YOUR_KEY"
{
"data": [
{ "id": "…", "file_name": "Lease Agreement Fetna - 599.pdf",
"document_type": "Absolute Net Lease Agreement",
"document_family_key": "grange conglomerate inc|100 hayden avenue…",
"total_fields": 142, "created_at": "2026-06-22T11:25:40Z" }
],
"pagination": { "limit": 2, "offset": 0, "total": 74, "returned": 2 }
}
abstracts:readFetch a single abstract in full, including all field_groups,
risk_flags, key_clauses, and
summary.
documents:readReturns a short-lived (15-minute) signed URL to the original source document. The file never moves and the URL expires automatically.
{ "id": "…", "file_name": "Lease Agreement Fetna - 599.pdf",
"file_type": "application/pdf",
"download_url": "https://…signed…", "expires_in": 900 }
Families
A family groups a primary document with its amendments, SNDAs, and related instruments (e.g. a lease and everything that modifies it). Members are deduplicated to the most recent abstract per source document and ordered by effective date.
families:readList families (paginated), most recently updated first.
families:readFetch one family by its family_key (URL-encode it), including
the rolled-up master_abstract and the ordered member list.
curl ".../v1/families/grange%20conglomerate%20inc%7C100%20hayden%20avenue…" \
-H "Authorization: Bearer aidi_YOUR_KEY"
{ "data": {
"family_name": "Grange conglomerate inc | 100 Hayden Avenue…",
"family_type": "lease", "member_count": 2,
"members": [
{ "file_name": "Lease Agreement Fetna - 599.pdf", "sequence_role": "original" },
{ "file_name": "SNDA and Amendment… - 600.pdf", "sequence_role": "amendment" }
] } }
Rendered documents
Pull the actual rendered files behind an abstract or family, not just the extracted data, as short-lived signed download URLs. Generated on demand (or reused if already rendered): the source Lease Abstract, an Excel workbook of the same, an Audit Exceptions workbook, and the Hawkeye Audit Report.
abstracts:readRender (or reuse) one artifact for this abstract and return a signed download URL.
| Query param | Type | Notes |
|---|---|---|
kind | string | one of abstract (Lease Abstract PDF, default), audit (Hawkeye Audit Report PDF), excel (Lease Abstract Excel), exceptions (Audit Exceptions Excel) |
curl ".../v1/abstracts/{id}/pdf?kind=audit" \
-H "Authorization: Bearer aidi_YOUR_KEY"
{ "abstract_id": "…", "family_key": "…", "kind": "audit",
"content_type": "application/pdf", "download_url": "https://…signed…", "expires_in": 900 }
422 if the abstract isn't part of a document family, the audit/exceptions artifacts are family-level.
families:readSame four kind options, rendered for the family's
master abstract instead of a single member document.
abstracts:readAll four artifacts in one call, Lease Abstract (PDF), Lease Abstract (Excel), Audit
Exceptions (Excel), and the Hawkeye Audit Report (PDF), each a signed
download_url, or an error string for
whichever one couldn't be produced (e.g. exceptions requires a saved ERP audit).
{ "abstract_id": "…", "family_key": "…",
"documents": [
{ "kind": "abstract", "label": "Lease Abstract", "content_type": "application/pdf", "download_url": "…", "expires_in": 900 },
{ "kind": "exceptions", "error": "No saved ERP audit for this family" }
] }
families:readThe same all-four-artifacts call, keyed by family instead of a single abstract.
Recovery audits
Audit-as-a-service for expense recoveries (CAM, operating expenses, real estate tax, insurance, utilities, capital). Submit the tenant's recovery terms, the property's actual expense pools, and what the ERP billed; AI.DI returns the deterministic entitled-versus-billed result, pool by pool, with the leakage broken out. Nothing is estimated by a model: the same inputs always return the same result. Both calls are scoped to the family and, when your key is bound to a tenant, to that tenant's families.
families:readRun a recovery true-up for the family and, by default, save it so it appears on the
family's Recoveries tab. URL-encode {key}.
| Body field | Type | Notes |
|---|---|---|
profile required | object | periodYear (integer) and pools (the lease terms per pool). Optional: tenantName, proRataShare (decimal, e.g. 0.1234) or tenantRSF + buildingRSF to derive it. |
profile.pools[] | array | poolId, category (cam, opex, retax, insurance, utilities, capital, other), method (net, base_year, expense_stop). Optional: baseYearAmount, expenseStopPSF, cap, grossUp, adminFeePct, exclusions, source, confidence. |
actualsByPool required | array | One entry per pool: poolId, category, grossAmount. Optional: variablePortion, controllableAmount, capitalAmortization, occupancy. |
erpBilled required | object | total (what the ERP billed for the period); optional byPool map of poolId to amount. |
save | boolean | default true. Send false to compute without saving. |
curl -X POST ".../v1/families/{key}/recovery-audit" \
-H "Authorization: Bearer aidi_YOUR_KEY" -H "Content-Type: application/json" \
-d '{
"profile": { "periodYear": 2025, "tenantRSF": 5794, "buildingRSF": 410000,
"pools": [{ "poolId": "cam", "category": "cam", "method": "net", "adminFeePct": 0.05 }] },
"actualsByPool": [{ "poolId": "cam", "category": "cam", "grossAmount": 1850000 }],
"erpBilled": { "total": 28000 }
}'
{ "family_key": "…", "audit_id": "…",
"result": { "periodYear": 2025, "proRataShare": 0.0141,
"totalEntitled": …, "totalBilled": 28000, "variance": …,
"pools": [ … ], "leakage": { … }, "warnings": [] } }
400 if profile, actualsByPool or erpBilled is missing;
404 if the family doesn't exist (or isn't in your environment);
502 compute_failed if the calculation could not run.
variance is entitled minus billed: negative means the tenant was over-billed.
families:readThe family's saved recovery audits, newest first (up to 100). Each row:
id, period_year, tenant_name,
total_entitled, total_billed, variance,
source (partner-api for audits run through this API),
created_by, created_at.
Search
searchSemantic + keyword (hybrid) search across the abstracted corpus.
| Body field | Type | Notes |
|---|---|---|
query required | string | Natural-language query |
limit | integer | 1 to 100, default 25 |
document_type | string | Restrict to a type |
document_category | string | Restrict to a category |
kinds | string[] | clause, risk_flag, field, summary |
curl -X POST ".../v1/search" \
-H "Authorization: Bearer aidi_YOUR_KEY" -H "Content-Type: application/json" \
-d '{"query":"rent escalation in net leases","limit":5}'
Platform resources
Beyond abstracts, the API exposes read access to the rest of the
document-of-record platform. Every resource below follows the same shape:
GET /v1/{resource} (paginated list with the standard
limit/offset + the filters noted)
and GET /v1/{resource}/{id} (single record). All return the
{ data, pagination } envelope and honor your key's scopes and
tenant binding. Sample/demo records are never returned.
Documents & storage scope documents:read
| Resource | Returns | Filters |
|---|---|---|
/v1/documents | Logical document records (type, status, version, dates, content hash, asset) | status, workflow_status, document_type_id, asset_id, content_hash, since |
/v1/storage-objects | Physical file registry (hash, size, MIME, tier, reference count) | storage_tier, content_hash |
/v1/duplicates | Near-duplicate candidates awaiting review (method, similarity) | status, source_document_id |
/v1/assets | Assets/properties documents are filed against | asset_type, status, state |
Vault & certification scope vault:read
| Resource | Returns | Filters |
|---|---|---|
/v1/vault | Immutable vault records (serial number, content hash, Sentry fingerprint, vaulted-at) | document_id, serial_number, client_id |
/v1/certificates | Document certificates (fingerprint, status, version, page count) | document_id, status |
Compliance scope compliance:read
| Resource | Returns | Filters |
|---|---|---|
/v1/compliance-requirements | Required document types per entity/period | client_id, property_id, document_type |
/v1/compliance-status | Current compliance state per requirement | requirement_id, document_id, is_compliant |
Connectors & hierarchy scope connectors:read
| Resource | Returns | Filters |
|---|---|---|
/v1/connectors | Integration connectors & health (credentials never exposed) | connector_type, is_active, health_status |
/v1/hierarchy-configurations | Hierarchy/taxonomy configurations | is_active, is_default |
/v1/hierarchy-levels | Levels within a configuration | configuration_id, level_key |
/v1/hierarchy-nodes | Nodes with document counts (tenant-scoped) | configuration_id, level_key, parent_id, status |
/v1/counterparties | Counterparty organizations | role, tier, room_id |
Access & audit scope audit:read
| Resource | Returns | Filters |
|---|---|---|
/v1/access-logs | Document access events | document_id, access_type, since |
/v1/audit-logs | Document lifecycle audit trail (events, actors) | document_id, event_type, node_id, since |
Reports scope reports:read
Reports (rent rolls, arrears, trial balances, P&L) ride the same submission rails as documents, validated, QC'd, and cross-referenced against the document set, not just stored files.
| Resource | Returns | Filters |
|---|---|---|
/v1/reports | Submitted reports (type, period, ingest status, source format) | node_id, report_type, ingest_status, since |
/v1/report-line-items | Parsed line-item data for a report | report_id |
/v1/report-qc-results | Quality-control checks run against a submitted report | report_id, packet_document_id, status |
/v1/report-correlation-results | Cross-references between report line items and the document set (e.g. a rent-roll tenant matched to its lease) | report_id, rule_id, match_status |
/v1/report-exceptions | Open discrepancies surfaced from QC/correlation, with first/last-seen tracking | node_id, report_id, status, exception_type |
Ingest a document (write)
Push a file into Gateway. The bytes run the full ingest pipeline, SHA-256 hash,
exact-duplicate check, text extraction, fuzzy fingerprinting, tiered storage, and record
creation, and fire the document.ingested webhook. This is the
only write endpoint; it needs the dedicated documents:write scope,
which you grant deliberately (it is not in the default read-only key).
Ingest is asynchronous: the call returns 202 Accepted right
away and the pipeline runs in the background. You get the final
document_id, content_hash, and
result from the document.ingested webhook
(or by polling GET /v1/documents), not in the immediate response.
Large files, upload directly to storage (recommended)
Do not send multi-MB files as base64 inside the JSON body, large request bodies do
not pass reliably through the API gateway (you may see a 504).
Instead, upload the raw bytes straight to storage with a short-lived signed URL, then
start ingest with just the path. Three steps:
1. Ask for a signed upload URL (tiny request):
documents:write| Body field | Type | Notes |
|---|---|---|
file_name required | string | Original file name |
mime_type required | string | e.g. application/pdf |
curl -X POST ".../v1/documents/upload-url" \
-H "Authorization: Bearer aidi_YOUR_KEY" -H "Content-Type: application/json" \
-d '{"file_name":"lease.pdf","mime_type":"application/pdf"}'
// 200 OK
{ "data": { "upload_url": "https://…/storage/v1/object/upload/sign/…?token=…",
"storage_path": "partner-ingest/…/lease.pdf",
"method": "PUT", "headers": { "Content-Type": "application/pdf", "x-upsert": "true" } } }
2. PUT the raw file bytes to upload_url
(this goes directly to storage and bypasses the API gateway, no size problem):
curl -X PUT "<upload_url>" \
-H "Content-Type: application/pdf" -H "x-upsert: true" \
--data-binary "@lease.pdf"
3. Start ingest with the returned storage_path:
documents:write| Body field | Type | Notes |
|---|---|---|
storage_path required* | string | Path returned by /v1/documents/upload-url after you PUT the bytes. Preferred for all but tiny files. |
file_base64 * | string | Alternative to storage_path, inline base64 for small files only (≤ 6 MB). Larger inline bodies are rejected (413). |
file_name required | string | Original file name |
mime_type required | string | e.g. application/pdf |
file_size | integer | Optional byte-size hint when using storage_path |
document_type | string | Optional type hint |
asset_id | uuid | File against an asset |
metadata | object | Arbitrary key/values stored with the document |
* Provide either storage_path (preferred) or file_base64 (small files only).
curl -X POST ".../v1/documents" \
-H "Authorization: Bearer aidi_YOUR_KEY" -H "Content-Type: application/json" \
-d '{"storage_path":"partner-ingest/…/lease.pdf","file_name":"lease.pdf","mime_type":"application/pdf"}'
// 202 Accepted, ingest runs asynchronously
{ "data": { "status": "processing", "storage_path": "partner-ingest/…/lease.pdf",
"file_name": "lease.pdf", "file_size": 123456 } }
When the pipeline finishes, the document.ingested webhook delivers
the document_id, content_hash, and
result, one of STORED_NEW,
EXACT_DUPLICATE (byte-identical file already stored, a record is
still created, the file is not re-stored), or NEAR_DUPLICATE.
Small files, inline base64 (≤ 6 MB)
For small files you can skip the signed-URL step and inline the bytes directly. Same async
202 response as above.
curl -X POST ".../v1/documents" \
-H "Authorization: Bearer aidi_YOUR_KEY" -H "Content-Type: application/json" \
-d '{"file_name":"lease.pdf","mime_type":"application/pdf","file_base64":"JVBERi0x..."}'
Webhooks: events & payloads
Subscribe to events instead of polling. Gateway POSTs a signed JSON payload to your URL when an event fires.
| Event | Status | Fires when |
|---|---|---|
abstract.completed | live | One or more abstracts finished processing (includes their ids, fetch with /v1/abstracts/{id}) |
family.reconciled | available | A document family's membership/rollup was (re)computed |
document.ingested | live | A document entered the pipeline (fires on every POST /v1/documents ingest) |
Delivery payload:
{
"event": "abstract.completed",
"delivery_id": "f1e2…",
"created_at": "2026-06-22T12:30:00Z",
"data": { "job_id": "…", "abstract_ids": ["…","…"], "count": 2 }
}
Headers on every delivery:
| Header | Value |
|---|---|
X-Imkore-Event | The event name |
X-Imkore-Delivery | Unique delivery id (for idempotency) |
X-Imkore-Timestamp | Unix seconds when sent |
X-Imkore-Signature | sha256=<hmac>, see verification |
Managing webhooks
webhooks:manageRegister an endpoint. The response includes a secret
(prefixed whsec_) returned only once, store it to
verify signatures.
curl -X POST ".../v1/webhooks" \
-H "Authorization: Bearer aidi_YOUR_KEY" -H "Content-Type: application/json" \
-d '{"url":"https://your-system.example.com/imkore","events":["abstract.completed"]}'
webhooks:manageList your registered webhooks (secrets are never returned again).
webhooks:manageRemove a webhook.
Verifying signatures
Recompute the HMAC-SHA256 of "{X-Imkore-Timestamp}.{rawRequestBody}"
using your webhook secret, then constant-time compare it to the hex in
X-Imkore-Signature. Reject if it doesn't match or the timestamp is
stale (> 5 min).
// Node.js
import crypto from 'crypto';
function verify(req, secret) {
const ts = req.headers['x-imkore-timestamp'];
const sig = req.headers['x-imkore-signature'].replace('sha256=', '');
const mac = crypto.createHmac('sha256', secret)
.update(ts + '.' + req.rawBody).digest('hex');
return crypto.timingSafeEqual(Buffer.from(mac), Buffer.from(sig));
}
# Python (Flask)
import hmac, hashlib
def verify(headers, raw_body, secret):
ts = headers['X-Imkore-Timestamp']
sig = headers['X-Imkore-Signature'].replace('sha256=', '')
mac = hmac.new(secret.encode(), f"{ts}.{raw_body}".encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(mac, sig)
Abstract object
| Field | Type | Description |
|---|---|---|
id | uuid | Stable abstract identifier |
file_name | string | Source file name |
file_size / file_type | int / string | Bytes and MIME type |
document_type | string | Classified type (e.g. lease, amendment) |
document_category | string | Higher-level grouping |
summary | string | Natural-language summary |
field_groups | array | Extracted fields grouped by section: [{group_name, fields:[{label,value}]}] |
risk_flags | array | Detected risks / unusual terms |
key_clauses | array | Notable clauses |
missing_or_unusual | array | Expected-but-absent or atypical items |
total_fields | int | Count of extracted fields |
classification_confidence | number | 0 to 100 confidence of the type classification |
document_family_key | string | Family this document belongs to (null if standalone) |
sequence_role | string | original, amendment, … |
effective_date_parsed | date | Parsed effective date |
source_connector_type / source_external_id | string | Where the document originated (e.g. filestar + its id) |
created_at | datetime | When the abstract was produced |
OpenAPI spec
A machine-readable OpenAPI 3.1 document is served (no auth) for code generation and import into Postman / Insomnia / Swagger UI:
GET https://nwbjvdemkdvlzdbntgnd.supabase.co/functions/v1/partner-api/openapi.json
Support & changelog
Questions or key provisioning: contact your imkore representative or developers@imkore.com.
| Version | Date | Notes |
|---|---|---|
| v1.2 | 2026-10-07 | Documented recovery audits (POST /v1/families/{key}/recovery-audit, GET /v1/families/{key}/recovery-audits); the MCP server now exposes 25 tools, adding tower-agreement terms and the Filestar DMS tools. Webhook events, scopes and every other endpoint are unchanged. |
| v1.1 | 2026-07-31 | Added the Reports domain (reports:read) and rendered-document endpoints (/pdf, /documents) for pulling the actual Lease Abstract, Audit Report, and Excel/exceptions artifacts, not just extracted data. |
| v1.0 | 2026-06-22 | Initial release: abstracts, families, documents, search, webhooks. |