Skip to content

API reference

Crawl controlled public websites, evaluate deterministic requirements and preserve independently verifiable audit evidence.

Quickstart

Create a site in the Ironfang portal, or create it directly through the API. The returned site ID is used when adding rules and starting audits.

curl -X POST https://api.ironfang.com/audit/v1/sites \
  -H "Authorization: Bearer $IRONFANG_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Finance",
    "url": "https://www.example.com"
  }'

The whole API is also a public Postman collection generated from the OpenAPI contract. Its Getting started folder creates a site, triggers an audit and polls it; the first request needs no key.

Run in PostmanView the collection

API keys

Automation can use a platform API key instead of a user token: mint one in the portal with the Ironfang Audit scopes it needs and send it as the bearer. A key acts only in the organisation it was minted for, and each scope stands for one permission - audit:read, audit:run, audit:manage, audit:evidence, audit:integrations, or audit:* for all of them. A key without an Ironfang Audit scope is refused with 403 insufficient_scope.

Deterministic rules

Rules retain their exact version in every result. Supported foundations include text contains or does not contain, regular expressions, element presence and text, links, metadata, HTTP status, HTTPS and response headers.

{
  "name": "Company number is present",
  "rule_type": "text_contains",
  "severity": "failure",
  "configuration": { "value": "12764014" }
}

A crawler failure is operational evidence state, not a failed compliance rule.

Audit lifecycle

queueddiscoveringcapturingfinalising evidencetimestampingcompleted

Audits are asynchronous. Use the returned permanent audit ID to poll the resource or consume HMAC-signed lifecycle webhooks. S3 exports and webhook delivery retry independently from evidence completion.

Evidence chain

Each page manifest contains the hashes of its raw screenshot, HTML, visible text, headers and network metadata. Page hashes become domain-separated Merkle leaves. Ironfang Audit signs the canonical audit manifest with Ed25519 and submits the audit root hash, not website content, to an RFC 3161 timestamp authority.

Raw captures remain immutable. Screenshot tiles and reports are presentation derivatives with provenance.

POST /verify/v1/bundles

Upload an evidence ZIP with Content-Type: application/zip, or verify it fully offline with the command-line verifier.

ironfang audit verify -bundle evidence.zip \
  -trusted-public-key audit-ed25519.pub \
  -tsa-ca tsa-chain.pem

The verifier recalculates artifact hashes, validates every page's Merkle membership and checks the Ironfang Audit signature against an independently trusted signing key. With a configured trusted TSA CA it also validates the RFC 3161 response. The key embedded in a ZIP is never accepted as its own trust anchor.

Linux, macOS and Windows builds are published under Apache 2.0. Each release includes checksums and signed Sigstore build provenance.

GET /v1/sites

List every Ironfang Audit site available to the current organisation.

POST /v1/sites

Register a public website and its crawl boundary. The response contains the permanent site ID used by rules and audits.

FieldTypeNotes
urlstringRequired public HTTP or HTTPS URL.
namestringDisplay name. Defaults to the website hostname.
timezonestringIANA timezone used by the controlled browser. Defaults to UTC.
crawl_policyobjectOptional allowed hosts, page limits and crawl controls.

GET /v1/sites/{siteId}

Return one site and its current crawl policy.

GET /v1/sites/{siteId}/rules

List the versioned deterministic rules configured for a site.

POST /v1/sites/{siteId}/rules

Create the first immutable version of a deterministic rule.

FieldTypeNotes
namestringRequired human-readable rule name.
descriptionstringOptional explanation of the requirement.
rule_typestringRequired deterministic evaluator type.
severitystringResult severity. Defaults to failure.
configurationobjectRequired configuration consumed by the selected evaluator.

GET /v1/audits

List audits for the current organisation. Supply site_id as a query parameter to restrict the response to one site.

POST /v1/sites/{siteId}/audits

Admit and queue an asynchronous audit. The pages it may capture are reserved against your billing account first: a crawl that your free allowance or usage limits only partly cover is given the pages that still fit and stops there, and one that nothing fits is refused with a billing code, so no audit exists. The audit is charged only for the page evidence it finalises. Supply an Idempotency-Key header when a client may retry the request.

FieldTypeNotes
trigger_typestringOrigin of the audit. Defaults to manual.
reasonstringOptional human-readable reason for the audit.

GET /v1/audits/{auditId}

Poll the audit status, compliance state, page counts and evidence root hash.

GET /v1/audits/{auditId}/pages

List captured page observations, rule outcomes and change classifications.

GET /v1/audits/{auditId}/evidence

Download the independently verifiable evidence ZIP after the audit reaches completed or partial. The response is application/zip.

GET /v1/artifacts/{artifactId}

Stream one tenant-scoped evidence artifact using its recorded media type.

GET /verify/v1/signing-keys

Return the public signing-key ledger used by the hosted verifier.

GET /v1/sites/{siteId}/monitors

List the monitors on a site: what is watched, how often, and when the next run is due.

POST /v1/sites/{siteId}/monitors

Create a monitor. mode is full_site (crawl) or url_set (the pages in urls); cadence is manual, daily, six_hourly or hourly, open to every account; anchor_minute and timezone fix the daily slot. Every scheduled run is a full audit with the same evidence chain as a manual one. Each run is admitted whole when its slot arrives; a run that does not fit your free allowance or usage limits is skipped with a monitor.skipped event whose reason is the billing code.

change_policy fieldTypeNotes
ignored_selectorsstring[]Regions removed from the comparison text (cookie banners, counters). Same selector subset as rules.
min_text_changenumber0-1 share of lines that must differ before the page counts as changed.
min_visual_changenumber0-1 share of sampled pixels.
ignore_numbersbooleanMask numbers and dates that tick over on every visit.
notify_only_on_rule_changebooleanOnly a rule outcome moving counts as material.

The policy shapes the comparison and the notification, never the record: the raw screenshot, HTML and visible text are stored untouched and the normalised text is kept alongside as a derivative.

POST /v1/monitors/{monitorId}/run

Run now. /pause, /resume and /archive change the status; PUT /urls replaces the page set; GET /runs lists the audits the monitor produced.

GET /v1/page-observations/{observationId}/comparison

What changed against the capture before it, computed from stored evidence under the audit's change policy: normalised text hunks with line numbers, text and visual scores, URL, title and HTTP status changes, rule outcomes that moved, screenshot artifact ids for both sides and whether the change was material. A first capture has no previous side and says why.

GET /v1/pages/{pageId}/observations

A page's history across audits, newest first. limit up to 200; pass the returned next_cursor to continue.

GET /v1/audits/{auditId}/pages?change=changed&compliance=fail

The page list takes change (new, changed, moved, unchanged), compliance (pass, fail, error), capture (captured, failed) and q (URL or title substring); each is a comma list. total is the unfiltered count.

GET /v1/events

The organisation's recent events - audit.started, audit.completed, audit.partial, audit.failed, change.detected, compliance.failed, monitor.skipped, evidence.expiring - with the payload each webhook received. Webhook envelopes carry schema_version and the event id as the idempotency key; an endpoint is disabled after 20 consecutive failures and re-enabled with PATCH /v1/webhooks/{endpointId} (enabled, events). DELETE retires an endpoint and keeps its history; POST .../deliveries/{deliveryId}/retry queues one delivery again.

GET /v1/notification-preferences

Which events are emailed, to whom, and whether at once or as one daily digest (07:00 UTC). PUT with events, recipients and digest; recipients must be current members of the organisation, and an API key may keep the list but not change it. Emails carry counts and a portal link, never captured content. GET /v1/notifications is the send log.

GET /v1/webhooks

List webhook endpoints registered for the current organisation.

POST /v1/webhooks

Register a public HTTPS endpoint. The HMAC signing secret is returned once in the creation response.

FieldTypeNotes
urlstringRequired public HTTPS destination.
eventsstring[]Defaults to audit.completed.

GET /v1/export-destinations

List configured S3 and S3-compatible evidence destinations.

POST /v1/export-destinations

Create an encrypted S3 destination. Required fields are region, bucket, access_key and secret_key. Optional fields include endpoint, prefix, path_style, session_token and export_mode.

POST /v1/audits/{auditId}/exports

Queue a completed audit for export using the supplied destination_id.

GET /v1/usage

This month's metered use, from the organisation's billing account: captures (audit.capture) and retention_extension (audit.retention_extension), each with the free allowance, what has been used, what admitted audits still hold and the charge so far before tax. Beside them are the facts that are not billing: sites against site_limit and max_pages_per_audit (capacity limits, the same for every account), standard_retention_days, hosted bytes and the next expiry, and a projection of the captures your active monitors may ask for this month.

POST /v1/retention/quotes

Price keeping the hosted evidence of chosen audits (audit_ids) or artifacts (artifact_ids) for months whole months past each current deadline. The quote is a fixed price from the rate catalogue, before tax, and can be bought until its expires_at, about fifteen minutes later. It moves nothing and charges nothing by itself. Needs audit:read.

POST /v1/retention/purchases

Buy a quote exactly as quoted, with its quote_id: every deadline moves and the usage is recorded in one step, or nothing happens. It needs a signed-in person with billing rights and a verified email address; API keys are refused. A quote that has expired, or whose evidence has changed since, answers 409 quote_expired: ask for a new one. Nothing renews by itself.

Errors

Every error an operation returns is one JSON shape: a stable code, a message for people, a link to this section and the request id.

{
  "error": {
    "code": "evidence_not_final",
    "message": "the evidence bundle is available after evidence finalisation",
    "docs": "https://ironfang.com/audit/docs#errors"
  },
  "request_id": "01a0c375-6b34-7c1e-9b52-4f0a8d3e6c17"
}

Branch on code and the HTTP status: the code is stable and machine-readable, while message is written for people and may change. New codes may be added, so treat one you do not recognise by its status class. docs links to this section. request_id repeats the X-Ironfang-Request-ID response header; quote it to support and we can find the exact request.

StatusCodeMeaning
400invalid_json, invalid_queryThe body is not one JSON value of the documented shape (unknown fields are refused), or a query parameter is unknown, repeated or malformed.
401unauthorized, invalid_api_keyNo bearer token, a token that does not verify, or an API key that is revoked or unknown.
403forbidden, insufficient_scopeThe key's scopes do not cover the route, the person's role in the organisation does not allow it, or the request names an organisation the credential cannot act in. When a permission is missing, the message names it.
404not_foundNo such site, rule, audit, finding, monitor, webhook or export destination in this organisation. An object that belongs to another organisation is reported the same way.
409evidence_not_final, monitor_archived, rule_retired, pack_already_installed, quote_expiredThe request conflicts with the state of the object: the evidence bundle was asked for before the audit reached completed or partial, the monitor is archived, the rule is retired, the rule pack is already installed on the site, or a retention quote can no longer be bought.
410evidence_expiredThe hosted evidence has passed its retention period. Hashes and signing metadata remain on the audit.
422invalid_url, unsafe_url, invalid_rule, invalid_cadence, invalid_webhook_urlThe JSON is well formed but a value is not acceptable: a URL that is not public HTTP or HTTPS (HTTPS only for a webhook) or does not resolve to a publicly routable host, a rule its evaluator cannot accept, an unknown cadence. The message says which value and why.
422site_limitThe organisation holds as many sites as the capacity limit allows. The limit is the same for every account; GET /v1/usage returns it as site_limit.
402, 429free_allowance_exhausted, account_budget_exhausted, product_budget_exhausted, paid_usage_paused, payment_requiredYour billing account refused the work before it started: the free allowance is used up without Pay as you go, a usage limit is reached, or paid usage is paused or waiting on a payment. Nothing was reserved or charged. Beside the code, the error carries product, meter and reset_at, when the free allowance renews.
429rate_limitedThe hosted verifier limits requests per client address. Wait for the seconds in Retry-After before trying again.
500, 503internal_error, permissions_unavailable, verifier_busyThe fault is ours. A 503 is temporary (permissions could not be checked, or the verifier is at capacity): try again shortly. For a 500, quote the request_id.

The list is not exhaustive. One 422 carries no error body: POST /verify/v1/bundles answers an invalid evidence ZIP with the verification report itself.

Machine interfaces

InterfaceDetails
Product pagehttps://ironfang.com/audit
Documentationhttps://ironfang.com/audit/docs
API base URLhttps://api.ironfang.com/audit
OpenAPI contracthttps://api.ironfang.com/audit/openapi.yaml. The same contract is served as JSON at https://api.ironfang.com/audit/openapi.json.
AuthenticationPlatform API key as a bearer token
Errorshttps://ironfang.com/audit/docs#errors. A JSON body with a stable code, a message, this link and the request id.
MCPAvailable. Read sites, audits, findings, rules and usage, and start a bounded audit of a site. Changing a rule, a site, a monitor or a finding stays in the portal and the REST API. Tools are named audit.*. MCP server reference; every tool and schema without a token at /.well-known/ironfang-mcp.json
Discovery/apis.json, /.well-known/api-catalog and /llms.txt

Launched as Auditwolf. The API paths, scopes, tool names and headers under that name were retired on 28 September 2026; only the identifiers below, recorded in signed evidence, keep it.