# 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.

***

### 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: 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.
