Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.anyformat.ai/llms.txt

Use this file to discover all available pages before exploring further.

Uses webhooks instead of polling — the recommended pattern for production integrations where contracts are processed asynchronously.

Workflow fields

We recommend creating this workflow in the anyformat platform where you can test with sample contracts and iterate on field descriptions. Copy the workflow ID to use with the API.
FieldTypeDescription
contract_typeenumType of agreement
effective_datedateWhen the contract takes effect
expiration_datedateWhen the contract expires
auto_renewalbooleanWhether the contract renews automatically
governing_lawstringJurisdiction governing the contract
termination_notice_daysintegerRequired notice period for termination
key_clausesmulti_selectWhich standard clauses are present
liability_capfloatMaximum liability amount if specified

Setup and submit

Python package + class names are provisional. pip install anyformat-sdk and from anyformat.sdk import Client work today, but both are expected to change before the official launch — pin the version you ship with.
The workflow is created once. After that, each new contract triggers one webhook delivery when processing finishes.
# 1. Create the workflow (once)
curl -X POST 'https://api.anyformat.ai/v2/workflows/' \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $ANYFORMAT_API_KEY" \
  -d '{
    "name": "Contract Analyzer",
    "description": "Extract key terms and clauses from contracts",
    "nodes": [
      {"id": "parse_1", "type": "parse"},
      {
        "id": "extract_1",
        "type": "extract",
        "extraction_schema": {
          "fields": [
            {
              "name": "contract_type",
              "description": "The type of legal agreement",
              "data_type": "enum",
              "enum_options": [
                {"name": "nda",               "description": "Non-disclosure agreement"},
                {"name": "service_agreement", "description": "Service or consulting agreement"},
                {"name": "employment",        "description": "Employment contract"},
                {"name": "lease",             "description": "Lease or rental agreement"},
                {"name": "partnership",       "description": "Partnership agreement"},
                {"name": "licensing",         "description": "Licensing agreement"}
              ]
            },
            {"name": "effective_date",          "description": "Date the contract takes effect",                          "data_type": "date"},
            {"name": "expiration_date",         "description": "Date the contract expires or terminates",                  "data_type": "date"},
            {"name": "auto_renewal",            "description": "Whether the contract automatically renews at expiration", "data_type": "boolean"},
            {"name": "governing_law",           "description": "State or jurisdiction whose laws govern this contract",   "data_type": "string"},
            {"name": "termination_notice_days", "description": "Number of days advance notice required to terminate",     "data_type": "integer"},
            {
              "name": "key_clauses",
              "description": "Standard contract clauses that are present in this agreement",
              "data_type": "multi_select",
              "enum_options": [
                {"name": "confidentiality",          "description": "Confidentiality or NDA clause"},
                {"name": "non_compete",              "description": "Non-compete restriction"},
                {"name": "indemnification",          "description": "Indemnification or hold-harmless clause"},
                {"name": "limitation_of_liability",  "description": "Cap on damages or liability"},
                {"name": "force_majeure",            "description": "Force majeure or act-of-God clause"},
                {"name": "arbitration",              "description": "Mandatory arbitration clause"},
                {"name": "intellectual_property",    "description": "IP ownership or assignment clause"},
                {"name": "non_solicitation",         "description": "Non-solicitation of employees or clients"}
              ]
            },
            {"name": "liability_cap", "description": "Maximum liability amount in dollars, if a cap is specified", "data_type": "float"}
          ]
        }
      }
    ],
    "edges": [{"source": "parse_1", "target": "extract_1"}]
  }'

# 2. Register a webhook (once) — store the returned secret
curl -X POST 'https://api.anyformat.ai/v2/webhooks/' \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $ANYFORMAT_API_KEY" \
  -d '{
    "url": "https://your-server.com/webhooks/anyformat",
    "events": ["extraction.completed", "extraction.failed"]
  }'
# → { "id": "...", "secret": "..." }   ← STORE THIS, it is only returned once

# 3. Submit a contract (no polling — the webhook will fire when done)
curl -X POST 'https://api.anyformat.ai/v2/workflows/WORKFLOW_ID/run/' \
  -H "Authorization: Bearer $ANYFORMAT_API_KEY" \
  -F 'file=@contract.pdf'

Handle the webhook callback

When processing completes, your server receives a POST with the collection_id and workflow_id. Verify the HMAC signature, then fetch the results from GET /v2/workflows/{workflow_id}/files/{collection_id}/results/. See Webhooks overview for the full payload schema and the verification recipe. A minimal Python (Flask) handler:
import hmac, hashlib, os, httpx
from flask import Flask, request, jsonify

app = Flask(__name__)
WEBHOOK_SECRET = os.environ["ANYFORMAT_WEBHOOK_SECRET"]
API_KEY = os.environ["ANYFORMAT_API_KEY"]

@app.route("/webhooks/anyformat", methods=["POST"])
def handle_webhook():
    signature = request.headers.get("X-Webhook-Signature", "")
    expected = hmac.new(WEBHOOK_SECRET.encode(), request.data, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(f"sha256={expected}", signature):
        return jsonify({"error": "invalid signature"}), 401

    event = request.json
    if event["event"] == "extraction.completed":
        data = event["data"]
        results = httpx.get(
            f"https://api.anyformat.ai/v2/workflows/{data['workflow_id']}"
            f"/files/{data['collection_id']}/results/",
            headers={"Authorization": f"Bearer {API_KEY}"},
        ).json()
        extraction = results["extractions"][0]["fields"]
        if "indemnification" not in extraction["key_clauses"]["value"]:
            print("WARNING: no indemnification clause")
    return jsonify({"status": "ok"}), 200
The TypeScript story is analogous: receive the POST in your server framework of choice (Express, Hono, Next route handler…), verify the signature with crypto.createHmac("sha256", secret), then fetch the results via fetch() or the SDK’s low-level client.

Tips

Webhook secrets are only returned at creation. Store the secret immediately. If lost, delete the webhook and create a new one.
  • Webhooks eliminate polling overhead and rate-limit consumption.
  • integer for notice periods lets you do calendar math directly.

Next steps

Webhooks

Set up, sign, and verify webhook deliveries

Field types

multi_select, enum, and other field shapes