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.
| Field | Type | Description |
|---|---|---|
contract_type | enum | Type of agreement |
effective_date | date | When the contract takes effect |
expiration_date | date | When the contract expires |
auto_renewal | boolean | Whether the contract renews automatically |
governing_law | string | Jurisdiction governing the contract |
termination_notice_days | integer | Required notice period for termination |
key_clauses | multi_select | Which standard clauses are present |
liability_cap | float | Maximum liability amount if specified |
Setup and submit
The workflow is created once. After that, each new contract triggers one webhook delivery when processing finishes.- curl
- TypeScript
- Python
# 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'
import { Anyformat, Schema } from "@anyformat/sdk";
const af = new Anyformat({ apiKey: process.env.ANYFORMAT_API_KEY! });
// 1. Build the workflow — the typed graph is assembled by the fluent builder.
const builder = af
.workflow("Contract Analyzer", "Extract key terms and clauses from contracts")
.parse()
.extract([
Schema.enum("contract_type", "The type of legal agreement", [
Schema.option("nda", "Non-disclosure agreement"),
Schema.option("service_agreement", "Service or consulting agreement"),
Schema.option("employment", "Employment contract"),
Schema.option("lease", "Lease or rental agreement"),
Schema.option("partnership", "Partnership agreement"),
Schema.option("licensing", "Licensing agreement"),
]),
Schema.date("effective_date", "Date the contract takes effect"),
Schema.date("expiration_date", "Date the contract expires or terminates"),
Schema.boolean("auto_renewal", "Whether the contract automatically renews at expiration"),
Schema.string("governing_law", "State or jurisdiction whose laws govern this contract"),
Schema.integer("termination_notice_days","Number of days advance notice required to terminate"),
Schema.multiSelect("key_clauses", "Standard contract clauses present in this agreement", [
Schema.option("confidentiality", "Confidentiality or NDA clause"),
Schema.option("non_compete", "Non-compete restriction"),
Schema.option("indemnification", "Indemnification or hold-harmless clause"),
Schema.option("limitation_of_liability", "Cap on damages or liability"),
Schema.option("force_majeure", "Force majeure or act-of-God clause"),
Schema.option("arbitration", "Mandatory arbitration clause"),
Schema.option("intellectual_property", "IP ownership or assignment clause"),
Schema.option("non_solicitation", "Non-solicitation of employees or clients"),
]),
Schema.float("liability_cap", "Maximum liability amount in dollars, if a cap is specified"),
]);
// 2. Create the workflow + a webhook (one-time setup). Webhook endpoints
// aren't on the SDK surface yet — call them via fetch and store the secret.
const workflow = await builder.create();
const webhookResponse = await fetch("https://api.anyformat.ai/v2/webhooks/", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ANYFORMAT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
url: "https://your-server.com/webhooks/anyformat",
events: ["extraction.completed", "extraction.failed"],
}),
});
const webhook = await webhookResponse.json();
// STORE webhook.secret securely — it is only returned once.
// 3. Submit each contract against the existing workflow handle. Skip .wait()
// — the webhook delivers the result.
const file: File = /* a File with .name set, e.g. new File([bytes], "contract.pdf") */;
const run = await workflow.run(file);
console.log(`Submitted ${run.id}; waiting for webhook…`);
import os
import httpx
from anyformat.sdk import Client
from anyformat.workflow import Schema
api_key = os.environ["ANYFORMAT_API_KEY"]
client = Client(api_key=api_key)
# 1. Create the workflow (once)
workflow = (
client.workflow("Contract Analyzer")
.parse()
.extract([
Schema.enum("contract_type", "The type of legal agreement", options=[
Schema.option("nda", "Non-disclosure agreement"),
Schema.option("service_agreement", "Service or consulting agreement"),
Schema.option("employment", "Employment contract"),
Schema.option("lease", "Lease or rental agreement"),
Schema.option("partnership", "Partnership agreement"),
Schema.option("licensing", "Licensing agreement"),
]),
Schema.date("effective_date", "Date the contract takes effect"),
Schema.date("expiration_date", "Date the contract expires or terminates"),
Schema.boolean("auto_renewal", "Whether the contract automatically renews at expiration"),
Schema.string("governing_law", "State or jurisdiction whose laws govern this contract"),
Schema.integer("termination_notice_days","Number of days advance notice required to terminate"),
Schema.multi_select("key_clauses", "Standard contract clauses present in this agreement", options=[
Schema.option("confidentiality", "Confidentiality or NDA clause"),
Schema.option("non_compete", "Non-compete restriction"),
Schema.option("indemnification", "Indemnification or hold-harmless clause"),
Schema.option("limitation_of_liability", "Cap on damages or liability"),
Schema.option("force_majeure", "Force majeure or act-of-God clause"),
Schema.option("arbitration", "Mandatory arbitration clause"),
Schema.option("intellectual_property", "IP ownership or assignment clause"),
Schema.option("non_solicitation", "Non-solicitation of employees or clients"),
]),
Schema.float("liability_cap", "Maximum liability amount in dollars, if a cap is specified"),
])
.create()
)
# 2. Register a webhook (once). Webhook endpoints aren't on the SDK surface
# yet — call them via httpx and store the returned secret.
webhook = httpx.post(
"https://api.anyformat.ai/v2/webhooks/",
headers={"Authorization": f"Bearer {api_key}"},
json={
"url": "https://your-server.com/webhooks/anyformat",
"events": ["extraction.completed", "extraction.failed"],
},
).json()
# STORE webhook["secret"] securely — it is only returned once.
# 3. Submit each contract. Skip .wait() — the webhook will fire when done.
workflow.run("contract.pdf")
Handle the webhook callback
When processing completes, your server receives a POST with thecollection_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
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.
integerfor 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