---
title: "The policy engine: before an action, POST {domain, action (read|cite|connect|transact|administer), agent?, template?, policy?} and receive a signed decision receipt (allow | warn | require_confirmation | block | unsupported) bound to the resolve answer it was made from. When the decision is not allow, alternatives[] names what to do instead (rescan, a machine file, a corroborated profile, an MCP endpoint that completed the handshake, human review), each from CrawlCheck’s own observations. A policy can only make the decision stricter. Templates: safe_citation, safe_data_retrieval, safe_api_connect, safe_mcp_tool, safe_commerce, safe_oauth, support_escalation"
method: POST
path: "/api/v1/preflight"
tags: ["Core"]
---

# The policy engine: before an action, POST {domain, action (read|cite|connect|transact|administer), agent?, template?, policy?} and receive a signed decision receipt (allow | warn | require_confirmation | block | unsupported) bound to the resolve answer it was made from. When the decision is not allow, alternatives\[\] names what to do instead (rescan, a machine file, a corroborated profile, an MCP endpoint that completed the handshake, human review), each from CrawlCheck’s own observations. A policy can only make the decision stricter. Templates: safe_citation, safe_data_retrieval, safe_api_connect, safe_mcp_tool, safe_commerce, safe_oauth, support_escalation

`POST /api/v1/preflight`

## Request

- Base URL: `https://crawlcheck.io`
- URL: `https://crawlcheck.io/api/v1/preflight`
- Auth: none required

## Request body

- object
  - `domain` string, required
  - `action` 'read' | 'cite' | 'connect' | 'transact' | 'administer'
  - `agent` string: your crawler token, e.g. GPTBot; checked against the site's robots.txt group for read and cite
  - `template` string
  - `policy` object
    - `max_age_hours` number
    - `on_warn` 'warn' | 'require_confirmation' | 'block'
    - `require_entity` boolean
    - `human_approval` string[]
    - `allow` string[]
    - `deny` string[]
  - `nonce` string: signed back in the receipt; bind a receipt to one request
  - `operation` object: the exact operation the decision is for, signed back as sent; a guard rejects a receipt used for any other
    - `method` 'GET' | 'HEAD' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS'
    - `url` string: absolute http(s) URL on the domain asked about
    - `tool` string
    - `tool_schema_sha256` string
    - `scopes` string[]

## Response `200`

crawlcheck-decision, signed (Ed25519, prefix crawlcheck-data-v1); verify offline with crawlcheck-verify.mjs

- DecisionV1: POST /api/v1/preflight: the policy engine's signed answer to 'may this agent take this action on this domain under this policy'. Signed like a resolve answer (Ed25519, prefix crawlcheck-data-v1); resolve.sha256 binds it to the resolve answer it was decided from.
  - `kind` 'crawlcheck-decision', required
  - `v` unknown, required
  - `decided_at` string, required
  - `expires_at` string, nullable
  - `clock_skew_s` integer
  - `nonce` string, nullable
  - `issued_at` string
  - `operation` object, nullable
  - `operation_sha256` string
  - `domain` string, required
  - `action` 'read' | 'cite' | 'connect' | 'transact' | 'administer', required
  - `agent` string, nullable
  - `template` string, nullable
  - `policy` object
    - `max_age_hours` number, nullable
    - `on_warn` 'warn' | 'block' | 'require_confirmation' | 'allow'
    - `require_entity` boolean
    - `human_approval` string[]
    - `allow` string[]
    - `deny` string[]
  - `engine` object
    - `version` string, required
    - `rules_sha256` string, required
  - `decision` 'allow' | 'warn' | 'require_confirmation' | 'block' | 'unsupported', required
  - `base_decision` 'allow' | 'warn' | 'require_confirmation' | 'block' | 'unsupported', required
  - `human_confirmation_required` boolean, required
  - `reason_codes` string[], required
  - `steps` object[], required
    - `rule` string, required
    - `effect` string, required
    - `why` string
  - `alternatives` unknown[]
    - unknown
  - `evidence` object, nullable
    - `report` string
    - `manifest_sha256` string
  - `resolve` object, nullable
    - `sha256` string
    - `kid` string
    - `leaf_hash` string
    - `archive` string
    - `status` 'measured' | 'partial' | 'not_measured' | 'opted_out' | 'withheld'
    - `answered_at` string
    - `observed_at` string, nullable
    - `report` string
    - `served` string
  - `meaning` string
  - `reading` string
  - `sha256` string, required
  - `signature` SignatureV1, required
    - `v` unknown, required
    - `alg` 'Ed25519', required
    - `kid` string, required
    - `over` string, required
    - `message` string, required
    - `sig` string, required
    - `signed_at` string, required
    - `key` object
      - `kid` string
      - `jwk` object
      - `published_in` string
    - `canonical` string

## Other responses

- `422`: no domain, an unknown action or template, or a policy that tries to loosen a decision

---

[API](https://skmtc.dev/crawlcheck/apis/crawlcheck-api.md) · [All operations](https://skmtc.dev/crawlcheck/apis/crawlcheck-api/llms.txt) · [OpenAPI document](https://skmtc.dev/crawlcheck/apis/crawlcheck-api/revisions/57acbd6d4489?raw)
