> For the complete documentation index, see [llms.txt](https://docs.icme.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.icme.io/api-reference/readme.md).

# Developer Platform API

**Base URL:** `https://api.icme.io/v1`&#x20;

**Authentication:** `X-API-Key: YOUR_KEY`&#x20;

***

Most endpoints return `application/json`. Three endpoints stream `text/event-stream` (SSE) instead:

| Endpoint                | Response |
| ----------------------- | -------- |
| `POST /v1/makeRules`    | SSE      |
| `POST /v1/checkIt`      | SSE      |
| `POST /v1/refinePolicy` | SSE      |

**All other endpoints return JSON.** If you need JSON-only for `checkIt`

#### SSE event format

Every SSE line has the form `data: {json}\n\n`. There are three event types, distinguished by the `step` field:

**Progress** — intermediate status updates:

```
data: {"step":"1/3","msg":"Analyzing action..."}
data: {"step":"2/3","msg":"Extracting values from action..."}
data: {"step":"3/3","msg":"Verifying against policy..."}
```

**Done** — final result. `step` is always `"done"`. The rest of the payload is endpoint-specific (see each endpoint below):

```
data: {"step":"done","check_id":"...","result":"SAT","detail":"..."}
```

**Error** — terminal failure. `step` is always `"error"`:

```
data: {"step":"error","code":"INSUFFICIENT_CREDITS","error":"Out of credits."}
```

The stream closes after a single `done` or `error` event. Keep-alive comments (`: keep-alive`) may appear between events — ignore lines that don't start with `data:` .

#### Parsing example (JavaScript)

```js
const res = await fetch("https://api.icme.io/v1/checkIt", {
  method: "POST",
  headers: { "X-API-Key": key, "Content-Type": "application/json" },
  body: JSON.stringify({ policy_id, action }),
});

const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = "";

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  buffer += decoder.decode(value, { stream: true });

  let lines = buffer.split("\n");
  buffer = lines.pop();          // keep incomplete line in buffer

  for (const line of lines) {
    if (!line.startsWith("data: ")) continue;
    const event = JSON.parse(line.slice(6));

    if (event.step === "error") throw new Error(event.error);
    if (event.step === "done")  return event;   // final result
    // else: progress — log or display event.msg
  }
}
```

#### Parsing example (Python)

```python
import requests, json

resp = requests.post(
    "https://api.icme.io/v1/checkIt",
    headers={"X-API-Key": key, "Content-Type": "application/json"},
    json={"policy_id": pid, "action": action},
    stream=True,
)

for line in resp.iter_lines(decode_unicode=True):
    if not line.startswith("data: "):
        continue
    event = json.loads(line[6:])

    if event["step"] == "error":
        raise Exception(event["error"])
    if event["step"] == "done":
        print(event)  # final result
        break
```

#### Parsing example (curl)

```bash
curl -N https://api.icme.io/v1/checkIt \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"policy_id":"...","action":"..."}' 2>/dev/null \
  | grep '^data: ' | tail -1 | sed 's/^data: //' | jq
```

***

### Accounts

#### `POST /v1/createUserX402`

Create an account via x402 USDC payment on Base. **$5.00 one-time fee.** No API key required.

The x402 middleware handles payment automatically. Send the request — if unpaid, you'll receive a `402` with payment requirements. Pay $5.00 USDC on Base, then retry with the `Payment-Signature` header. Returns API key and 325 starting credits.

> **Save your `api_key` immediately.** It is shown only once. Use it as the `X-API-Key` header for all authenticated endpoints.

#### `POST /v1/createUserCard`

Create an account via card payment. No crypto required. Returns a Stripe Checkout URL.

After paying, call `GET /v1/session/{session_id}` to retrieve your API key.

#### `POST /v1/createUser`

Create an account via USDC on Base (Stripe deposit flow). **$5.00 one-time fee.** Gives 325 credits.

Call without `stripe_payment_intent_id` to receive a deposit address. Send exactly $5.00 USDC to `payTo` on Base, then retry with `stripe_payment_intent_id`.

#### `POST /v1/topUpX402`

Add 500 credits via x402 USDC payment on Base. **$5.00.** Requires `X-API-Key` header.

No request body needed. The x402 middleware handles payment — retry with `Payment-Signature` after paying.

#### `POST /v1/topUpCard`

Add credits via card payment. No crypto required. Returns a Stripe Checkout URL.

After paying, call `GET /v1/session/{session_id}` to confirm credits were added.

#### `POST /v1/topUp`

Add credits via USDC on Base (Stripe deposit flow). Call with empty body to see tiers and current balance.

| Amount | Credits | Bonus |
| ------ | ------- | ----- |
| $5     | 500     | —     |
| $10    | 1,050   | +5%   |
| $25    | 2,750   | +10%  |
| $50    | 5,750   | +15%  |
| $100   | 12,000  | +20%  |

#### `GET /v1/session/{session_id}`

Poll after a card payment to retrieve account info or confirm credits. No API key required.

Returns `status: pending` while payment is processing, `status: complete` once done. For signup, the response includes `api_key` — save it, it will not be shown again.

#### `GET /v1/me`

Return the authenticated user's account info and current credit balance. Requires `X-API-Key` header.

```bash
curl -s https://api.icme.io/v1/me \
  -H "X-API-Key: YOUR_KEY"
```

```json
{
  "user_id": "a1b2c3d4-...",
  "username": "alice",
  "credits": 312,
  "admin": false
}
```

Use this to check your remaining credits before making calls. The balance is read from the database at request time, so it always reflects the latest value.

#### `GET /v1/me/policies`

List all policies owned by the authenticated user, newest first. Requires `X-API-Key` header.

```bash
curl -s https://api.icme.io/v1/me/policies \
  -H "X-API-Key: YOUR_KEY"
```

```json
{
  "user_id": "a1b2c3d4-...",
  "username": "alice",
  "count": 2,
  "policies": [
    {
      "policy_id": "f6e3cd15-9e28-45c4-9f4c-683edd63e468",
      "original_text": "Block any transfer over $500 to an unverified wallet.",
      "rule_count": 3,
      "aws_guardrail_id": "arn:aws:...",
      "oxiz_status": "SAT",
      "created_at": "2026-04-10T18:30:00Z"
    },
    {
      "policy_id": "b2c3d4e5-...",
      "original_text": "Require manager approval for expenses above $1000.",
      "rule_count": 2,
      "aws_guardrail_id": null,
      "oxiz_status": null,
      "created_at": "2026-04-08T12:00:00Z"
    }
  ]
}
```

Returns a trimmed projection per policy — use `GET /v1/policy/{id}/scenarios` or `GET /v1/policy/{id}/variables` for full detail on a specific policy.

***

### Policy

#### `POST /v1/makeRules`

Compile a natural language policy to formal logic. **300 credits.** Returns `text/event-stream` (SSE).

Write your guardrail policy in plain English. Preflight compiles it to SMT-LIB formal logic and returns a `policy_id` + scenarios for review.

`scenarios` are generated by ICME Preflight from your compiled rules, sorted to surface the most likely-to-be-wrong variable combinations first. Review them before using the policy in production. See **Battle Testing Rules**.

**SSE progress steps:** `1/6` → `2/6` → … → `6/6` (cloud mode) or `1/3` → `2/3` → `3/3` (local mode).

**Done payload:**

```json
{
  "step": "done",
  "policy_id": "f6e3cd15-...",
  "status": "ok",
  "rule_count": 3,
  "generation_time_ms": 4200,
  "scenarios": 3,
  "next_steps": {
    "get_scenarios": "GET /v1/policy/{id}/scenarios",
    "submit_feedback": "POST /v1/submitScenarioFeedback",
    "run_tests": "POST /v1/runPolicyTests",
    "docs": "https://docs.icme.io/documentation/battle-testing-rules"
  }
}
```

**Credits and failure:** If compilation fails (SSE `error` event), the 300 credits are refunded automatically. If the stream drops or your client fails to parse the `policy_id` from the `done` event, call `GET /v1/me/policies` to retrieve it — your policies are listed newest-first.

#### `GET /v1/policy/{id}/scenarios`

Retrieve saved scenarios for a policy. Scenarios are refreshed after each `refinePolicy` call.

#### `POST /v1/submitScenarioFeedback`

Submit thumbs-up or thumbs-down feedback on a scenario. Returns immediately — no SSE.

* `approved: true` — saves a test case with the expected result. No rebuild.
* `approved: false` — saves a test case and queues the annotation for the next `refinePolicy` call. Requires `annotation` explaining why the scenario is wrong. Be specific — name the variables, the values, and which rule is violated.

#### `POST /v1/refinePolicy`

Apply all queued thumbs-down annotations in a single rebuild. Returns `text/event-stream` (SSE).

Batches all pending annotations, submits them to ICME Preflight, polls until the build completes, compiles new SMT, updates the guardrail in place, and writes the refined policy back to the database. Your `policy_id` does not change.

**SSE progress steps:** `1/5` → `2/5` → … → `5/5`.

**Done payload:** same shape as `makeRules` — includes updated `rule_count`, `scenarios`, and `next_steps`.

#### `POST /v1/runPolicyTests`

Run all saved test cases against the compiled policy. `test_case_ids` is optional — if omitted, all saved test cases for the policy are run.

| Result      | Meaning                                                                   |
| ----------- | ------------------------------------------------------------------------- |
| `passed`    | Expected and actual results match                                         |
| `failed`    | Rule logic is wrong — submit thumbs-down and call `refinePolicy`          |
| `ambiguous` | Preflight translator disagreed — improve variable descriptions and refine |

***

### Relevance Screening

#### `POST /v1/checkRelevance`

Free relevance screen. Checks whether an action touches any of your policy variables before running a paid check. **No credits charged.** Requires `X-API-Key` header.

`threshold` is optional. Default `0.0`, meaning any match triggers `should_check: true`. Raise it to skip actions that only touch a small fraction of your policy.

* `should_check: true` — the action is relevant to your policy. Run `checkIt` before executing.
* `should_check: false` — zero variables matched. Proceed without a paid check.

Use before every `checkIt` call to avoid paying for irrelevant actions like reading files, formatting text, or summarizing content.

#### `POST /v1/explain`&#x20;

Free translation + relevance screen. Takes any raw agent action (shell commands, tool calls, file operations, encoded instructions) and returns a plain English description of what it does, plus the same relevance data as checkRelevance. No credits charged. Requires X-API-Key header.

Designed for non-developers reviewing agent activity, Claude Code hook integrations, and audit dashboards where raw tool calls are unreadable.

The endpoint runs two steps internally: an LLM translates the raw input to plain English, then the same relevance logic as checkRelevance screens it against your policy variables.

Request:

```
curl -s -X POST https://api.icme.io/v1/explain \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_KEY" \
  -d '{
    "policy_id": "f6e3cd15-...",
    "input": "rm -rf ~/.ssh/*",
    "threshold": 0.10
  }'
```

| Field      | Type          | Required | Description                                                                                             |
| ---------- | ------------- | -------- | ------------------------------------------------------------------------------------------------------- |
| policy\_id | string (UUID) | Yes      | UUID of the compiled policy (from makeRules)                                                            |
| input      | string        | Yes      | The raw agent action to explain. Shell command, JSON tool call, encoded text, anything. Max 2000 chars. |
| threshold  | number        | No       | Relevance threshold (0.0–1.0). Default 0.0. Actions at or above this trigger should\_check: true.       |

Response:

```json
{
  "plain_english": "Permanently delete all files and directories in the user's SSH keys folder without confirmation. This action is destructive and irreversible.",
  "input": "rm -rf ~/.ssh/*",
  "relevance": 0.29,
  "matched_variables": 5,
  "total_variables": 17,
  "matched": [
    "skillExecutesShellCommands",
    "outboundDataTransmission",
    "modifiesAuthenticationFiles",
    "modifiesApiKeys",
    "externalEmailAllowed"
  ],
  "should_check": true,
  "threshold": 0.1,
  "time_ms": 1776
}
```

| Field              | Type      | Description                                                                      |
| ------------------ | --------- | -------------------------------------------------------------------------------- |
| plain\_english     | string    | Human-readable description of what the action does                               |
| input              | string    | The original input that was explained                                            |
| relevance          | number    | Fraction of policy input variables touched (0.0–1.0)                             |
| matched\_variables | number    | Count of policy variables the action touches                                     |
| total\_variables   | number    | Total input variables in the policy                                              |
| matched            | string\[] | Names of the matched policy variables                                            |
| should\_check      | boolean   | Whether the action exceeds the threshold. If true, run checkIt before executing. |
| threshold          | number    | The threshold that was used                                                      |
| time\_ms           | number    | Processing time in milliseconds                                                  |

When to use explain vs checkRelevance:

* Use explain when the input is opaque (shell commands, tool call JSON, encoded text) and you need a human-readable translation.
* Use checkRelevance when you already have a plain English action string and just need the relevance screen.

Both are free. Both return should\_check. The only difference is explain adds the translation step and returns plain\_english.

Typical flow with Claude Code hooks:

```
PreToolUse hook fires
        |
        v
POST /v1/explain (free)
        |
  should_check: false --> allow, zero cost
  should_check: true  --> POST /v1/checkIt (1 credit)
        |
    SAT --> allow
    UNSAT --> block
```

Install the hook with one command:

bash

```bash
npx icme-claude-preflight init
```

***

#### `GET /v1/policy/{id}`

Retrieve a policy's original text, compiled SMT-LIB, and parsed rules. No credits charged. Requires X-API-Key header.

```bash
curl -s https://api.icme.io/v1/policy/{id} \
  -H "X-API-Key: YOUR_KEY" | jq .
```

```json
{
  "policy_id": "id-...",
  "original_text": "1. If a shell command sends data to an external server...",
  "smt": "(set-logic ALL)\n(declare-const transferAmount Real)\n...",
  "rules_parsed": [
    {
      "id": "rule_1",
      "description": "If outbound data transmission is occurring..."
    }
  ],
  "rule_count": 3,
  "created_at": "2026-04-10T18:30:00Z"
}
```

| Field          | Type              | Description                                         |
| -------------- | ----------------- | --------------------------------------------------- |
| policy\_id     | string (UUID)     | The policy identifier                               |
| original\_text | string            | The plain English policy you submitted to makeRules |
| smt            | string            | The compiled SMT-LIB formal logic                   |
| rules\_parsed  | array             | The individual rules extracted during compilation   |
| rule\_count    | integer           | Number of rules in the policy                       |
| created\_at    | string (ISO 8601) | When the policy was created                         |

Use this to inspect what your policy compiled to, debug unexpected checkIt results, or export the SMT-LIB for external tooling.

### Checking Actions

#### `POST /v1/checkItPaid`

Check an agent action against a compiled policy via x402 payment. **$0.10 per call.** No API key required.

`result` is `SAT` (permitted) or `UNSAT` (blocked). Every decision returns a `check_id` which serves as a cryptographic audit receipt.

> **Writing action strings:** End every action string with an explicit claim — *"Therefore this transfer is permitted."* State every policy variable explicitly in the action. Do not rely on the extractor to infer missing values.

#### `POST /v1/checkIt`

Check an agent action against a compiled policy. **1 credit.** Requires `X-API-Key` header. Returns `text/event-stream` (SSE).

Same as `checkItPaid` but authenticated via API key and deducts 1 credit. This is the cheapest path if you already have credits from signup (325 free) or `topUp`.

**SSE progress steps:** `1/3` → `2/3` → `3/3`.

**Done payload:**

```json
{
  "step": "done",
  "check_id": "a1b2c3d4-...",
  "result": "SAT",
  "detail": "All constraints satisfied.",
  "extracted": { "amount": 100, "recipient": "0xABC..." },
  "verification_time_ms": 320,
  "llm_result": "SAT",
  "ar_result": "SAT",
  "ar_detail": "...",
  "z3_result": "SAT",
  "zk_proof_id": "e5f6a7b8-...",
  "zk_proof_url": "https://api.icme.io/v1/proof/e5f6a7b8-..."
}
```

If you need a plain JSON response instead of SSE, use `POST /v1/checkItProd` — same logic, single JSON response, no streaming.

#### `POST /v1/checkItProd`

Same as `checkIt` but returns a single `application/json` response instead of SSE. **1 credit.** Requires `X-API-Key` header. Uses the cloud reasoning engine for extraction and verification.

```json
{
  "check_id": "a1b2c3d4-...",
  "result": "SAT",
  "detail": "All constraints satisfied.",
  "extracted": { "amount": 100, "recipient": "0xABC..." },
  "verification_time_ms": 320,
  "zk_proof_id": "e5f6a7b8-...",
  "zk_proof_url": "https://api.icme.io/v1/proof/e5f6a7b8-..."
}
```

Best choice when you want a simple `await res.json()` workflow without SSE parsing.

#### `POST /v1/verify`

Check structured values directly against a policy. No LLM extraction. **1 credit.** Requires `X-API-Key` header.

Returns a minimal `ALLOWED` or `BLOCKED` verdict.

#### `POST /v1/verifyPaid`

Check any policy with no account. **$0.10 per call** via USDC on Base (Stripe deposit flow). No API key required.

Call without payment header to receive a deposit address. Send exactly $0.10 USDC to `payTo` on Base, then retry.

***

### ZK Proofs

Every `checkIt`/`checkItPaid` call triggers zero-knowledge proof generation in the background. The `proof_id` and `proof_url` are returned immediately in the done event, but **the proof itself takes 30–60 seconds to generate.** `GET /v1/proof/{id}` returns `404` until generation completes.

#### Polling for proof readiness

Poll `GET /v1/proof/{id}` until it returns `200`. Recommended interval: **5 seconds**, timeout after **120 seconds**.

```js
async function waitForProof(proofId, apiKey, { timeoutMs = 120_000, intervalMs = 5_000 } = {}) {
  const start = Date.now();
  while (Date.now() - start < timeoutMs) {
    const res = await fetch(`https://api.icme.io/v1/proof/${proofId}`, {
      headers: { "X-API-Key": apiKey },
    });
    if (res.ok) return await res.json();       // proof ready
    if (res.status !== 404) throw new Error(`Unexpected ${res.status}`);
    await new Promise(r => setTimeout(r, intervalMs));  // not ready yet
  }
  throw new Error(`Proof not ready after ${timeoutMs / 1000}s`);
}
```

| Status | Meaning                                     |
| ------ | ------------------------------------------- |
| `404`  | Proof is still generating — keep polling    |
| `200`  | Proof is ready — response contains metadata |
| `409`  | Proof was already consumed (single-use)     |

#### `GET /v1/proof/{id}`

Retrieve proof metadata including validity, trace length, and timing. Add `?include_bytes=true` to include the raw proof hex.

#### `GET /v1/proof/{id}/download`

Download raw ZK proof binary. Single-use — marks the proof as consumed.

#### `POST /v1/verifyProof`

Verify a zero-knowledge proof. No additional cost — proof generation was paid for by the original check.

**Single-use:** each proof can only be verified once. Subsequent calls return `409`.

***

### Payment Flows

#### x402 (Recommended for Agents)

Fully autonomous — no accounts, no API keys for `checkItPaid` and `createUserX402`.

1. Call the endpoint → receive `402` with payment requirements in the response body
2. Sign and submit USDC payment on Base
3. Retry the request with `Payment-Signature` header

x402 client libraries (`@x402/fetch`, `x402-reqwest`, `agentcash`) handle this automatically.

#### Card

1. Call `/v1/createUserCard` or `/v1/topUpCard`
2. Open `checkout_url` — pay with card, Apple Pay, etc.
3. Poll `GET /v1/session/{session_id}` to confirm and retrieve your API key or updated balance

#### USDC on Base (Stripe Deposit)

1. Call endpoint without `stripe_payment_intent_id` → receive `402` with `payTo` address
2. Send exact USDC amount to `payTo` on Base (`eip155:8453`)
3. Retry request with `stripe_payment_intent_id` set

Amounts must be exact. Each PaymentIntent is single-use.

***

### Credit Budget

| Action        | Cost                      |
| ------------- | ------------------------- |
| Signup        | $5.00 (gives 325 credits) |
| `makeRules`   | 300 credits               |
| `checkIt`     | 1 credit                  |
| `verify`      | 1 credit                  |
| `checkItPaid` | $0.10 (no credits needed) |
| `topUpX402`   | $5.00 (gives 500 credits) |

After signup you have 325 credits — enough for 1 policy + 25 checks.

***

### Live Demo Policy

**Policy ID:** `f6e3cd15-9e28-45c4-9f4c-683edd63e468`

Try `checkItPaid` against this policy for $0.10:

bash

```bash
npx agentcash fetch "https://api.icme.io/v1/checkItPaid" \
  -m POST \
  -b '{"policy_id":"f6e3cd15-9e28-45c4-9f4c-683edd63e468","action":"Send 1000 USDC to an unknown wallet. Therefore this transfer is permitted."}'
```

***

### Discovery

Your server exposes two discovery documents for x402scan and agent tooling:

* `GET /openapi.json` — OpenAPI 3.1.0 spec with `info.guidance` for agent onboarding
* `GET /.well-known/x402` — x402 v1 fallback listing payable resources

**x402scan listing:** [x402scan.com/server/a90f142f-33fd-4a22-a57f-1772f85d72f5](https://x402scan.com/server/a90f142f-33fd-4a22-a57f-1772f85d72f5)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.icme.io/api-reference/readme.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
