> ## Documentation Index
> Fetch the complete documentation index at: https://docs.precipiq.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Precipiq REST API reference

> Authenticate with X-Precipiq-Key, explore every endpoint under /api/v1/, and understand envelopes, rate limits, and common call patterns.

The Precipiq REST API is the foundation of the AI Consequences Ledger. Every endpoint lives under `https://api.precipiq.dev/api/v1/` and returns a consistent JSON envelope so you can handle success and error paths the same way across your codebase.

## Authentication

Every request requires an `X-Precipiq-Key` header. Keys come in two flavors:

* `pq_live_…` — production keys that write to your live ledger
* `pq_test_…` — sandbox keys for integration testing; data is isolated and never appears in production reports

```bash theme={null}
curl -s https://api.precipiq.dev/api/v1/decisions \
  -H 'X-Precipiq-Key: pq_test_demo_key_REPLACE_ME'
```

Missing or invalid keys return **401** with this body — the message is identical whether the header is absent or wrong, so an attacker cannot enumerate valid keys by response shape:

```json theme={null}
{
  "error": {
    "code": "unauthorized",
    "message": "invalid or missing api key"
  }
}
```

## Response envelope

All successful responses share the same outer shape. The `data` field contains the endpoint-specific payload; `meta` carries tracing information you can include in support tickets.

```json theme={null}
{
  "data": { },
  "meta": {
    "request_id": "550e8400-e29b-41d4-a716-446655440000",
    "timestamp": "2026-04-16T12:34:56.789012Z"
  }
}
```

Every response also sets an `X-Request-ID` header that mirrors `meta.request_id`. Include it when opening a support ticket.

Error responses use a separate envelope:

```json theme={null}
{
  "error": {
    "code": "validation_error",
    "message": "confidence must be ≤ 1.0",
    "details": { }
  }
}
```

The optional `details` object contains field-level validation errors when `code` is `validation_error`.

## Rate limits

Limits are applied per organization. Exceeding a limit returns **429** with a `Retry-After` value in the response.

| Bucket              | Limit             | Endpoints                                                                         |
| ------------------- | ----------------- | --------------------------------------------------------------------------------- |
| Single ingestion    | 100 req / s       | `POST /decisions`, `POST /financial-events`, `POST /links`, `DELETE /links/{id}`  |
| Batch ingestion     | 10 req / s        | `POST /decisions/batch`                                                           |
| Reads               | 1,000 req / s     | `GET /decisions`, `GET /links`, `GET /analytics/*`, `GET /decisions/chain/verify` |
| WebSocket handshake | 30 req / min / IP | `/ws/v1/stream`                                                                   |

<Tip>Use the batch endpoint (`POST /decisions/batch`) when ingesting bursts of decisions. You get 10 large calls per second instead of 100 individual ones, and the server preserves hash-chain order inside each batch.</Tip>

## Endpoints at a glance

Browse all endpoints by tag in the interactive reference below, or jump straight to a group:

* **decisions** — `/api/v1/decisions`, `/decisions/batch`, `/decisions/{id}`, `/decisions/chain/verify`
* **financial-events** — `/api/v1/financial-events`, `/financial-events/webhook/stripe`
* **consequence-links** — `/api/v1/links`, `/links/{id}`, `/links/{id}/confirm`, `/links/{id}/reject`
* **analytics** — `/api/v1/analytics/ai-pnl`, `/analytics/timeline`, `/analytics/liability`
* **exports** — `/api/v1/exports`, `/exports/{id}`, `/exports/{id}/download`
* **websocket** — `/ws/v1/stream`

## Common calls

Every example below uses the placeholder key `pq_test_demo_key_REPLACE_ME` — substitute your own key from the dashboard before running.

### Ingest a single decision

`POST /api/v1/decisions`

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.precipiq.dev/api/v1/decisions \
    -H 'X-Precipiq-Key: pq_test_demo_key_REPLACE_ME' \
    -H 'Content-Type: application/json' \
    -d '{
      "agent_id": "pricing-bot",
      "action_type": "discount_offer",
      "inputs":  {"customer_id": "cust_123", "tier": "gold"},
      "outputs": {"discount_pct": 15},
      "confidence": 0.82,
      "human_in_loop": false
    }'
  ```

  ```python Python theme={null}
  import httpx

  r = httpx.post(
      "https://api.precipiq.dev/api/v1/decisions",
      headers={"X-Precipiq-Key": "pq_test_demo_key_REPLACE_ME"},
      json={
          "agent_id": "pricing-bot",
          "action_type": "discount_offer",
          "inputs":  {"customer_id": "cust_123", "tier": "gold"},
          "outputs": {"discount_pct": 15},
          "confidence": 0.82,
          "human_in_loop": False,
      },
      timeout=10.0,
  )
  r.raise_for_status()
  print(r.json()["data"]["decision_id"])
  ```

  ```typescript TypeScript theme={null}
  const res = await fetch(
    'https://api.precipiq.dev/api/v1/decisions',
    {
      method: 'POST',
      headers: {
        'X-Precipiq-Key': 'pq_test_demo_key_REPLACE_ME',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        agent_id: 'pricing-bot',
        action_type: 'discount_offer',
        inputs:  { customer_id: 'cust_123', tier: 'gold' },
        outputs: { discount_pct: 15 },
        confidence: 0.82,
        human_in_loop: false,
      }),
    },
  );
  if (!res.ok) throw new Error(`ingest failed: ${res.status}`);
  const body = await res.json();
  console.log(body.data.decision_id);
  ```
</CodeGroup>

### Ingest a batch of decisions

`POST /api/v1/decisions/batch` — send up to 100 decisions in a single call.

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.precipiq.dev/api/v1/decisions/batch \
    -H 'X-Precipiq-Key: pq_test_demo_key_REPLACE_ME' \
    -H 'Content-Type: application/json' \
    -d '{
      "decisions": [
        {"agent_id":"a","action_type":"t","inputs":{},"outputs":{},"confidence":0.9,"human_in_loop":false},
        {"agent_id":"a","action_type":"t","inputs":{},"outputs":{},"confidence":0.8,"human_in_loop":false}
      ]
    }'
  ```

  ```python Python theme={null}
  httpx.post(
      "https://api.precipiq.dev/api/v1/decisions/batch",
      headers={"X-Precipiq-Key": "pq_test_demo_key_REPLACE_ME"},
      json={"decisions": [decision1, decision2]},
      timeout=10.0,
  ).raise_for_status()
  ```
</CodeGroup>

### List decisions with cursor pagination

`GET /api/v1/decisions?agent_id=&action_type=&start=&end=&cursor=`

```bash curl theme={null}
curl -s 'https://api.precipiq.dev/api/v1/decisions?agent_id=pricing-bot' \
  -H 'X-Precipiq-Key: pq_test_demo_key_REPLACE_ME'
```

The response's `data.next_cursor` is an opaque string. Pass it back verbatim as `?cursor=…` to fetch the next page. Results are ordered newest first (`timestamp DESC, id DESC`), and the cursor guarantees no rows are skipped or duplicated across pages.

### Verify the hash chain

`GET /api/v1/decisions/chain/verify?start_date=&end_date=`

```bash curl theme={null}
curl -s https://api.precipiq.dev/api/v1/decisions/chain/verify \
  -H 'X-Precipiq-Key: pq_test_demo_key_REPLACE_ME'
```

Returns `is_valid`, `records_checked`, `first_broken_link`, and `verification_timestamp`. See [Hash Chain](/concepts/hash-chain) for the semantics.

### Ingest a financial event

`POST /api/v1/financial-events`

```bash curl theme={null}
curl -X POST https://api.precipiq.dev/api/v1/financial-events \
  -H 'X-Precipiq-Key: pq_test_demo_key_REPLACE_ME' \
  -H 'Content-Type: application/json' \
  -d '{
    "source": "internal",
    "event_type": "payment",
    "amount": "149.00",
    "currency": "USD",
    "external_id": "invoice_0042",
    "timestamp": "2026-04-16T12:35:02Z"
  }'
```

### Link a decision to a financial event

`POST /api/v1/links`

```bash curl theme={null}
curl -X POST https://api.precipiq.dev/api/v1/links \
  -H 'X-Precipiq-Key: pq_test_demo_key_REPLACE_ME' \
  -H 'Content-Type: application/json' \
  -d '{
    "decision_id":         "d5e7a3b0-0000-0000-0000-000000000001",
    "financial_event_id":  "fev_abc",
    "correlation_strength": 1.0,
    "attribution_method":   "direct",
    "link_type":            "revenue",
    "notes":                "upsell offer accepted"
  }'
```

### Confirm or reject a suggested link

`POST /api/v1/links/{id}/confirm` and `POST /api/v1/links/{id}/reject`

These endpoints are used when a human reviews a link with status `ML_SUGGESTED`. Confirming flips it to `ML_INFERRED` and nudges the Bayesian priors; rejecting soft-deletes it and nudges them the other way.

```bash curl theme={null}
curl -X POST https://api.precipiq.dev/api/v1/links/${LINK_ID}/confirm \
  -H 'X-Precipiq-Key: pq_test_demo_key_REPLACE_ME'
```

### Fetch the AI P\&L

`GET /api/v1/analytics/ai-pnl?start=&end=&agent_id=`

```bash curl theme={null}
curl -s 'https://api.precipiq.dev/api/v1/analytics/ai-pnl?start=2026-04-01&end=2026-04-30' \
  -H 'X-Precipiq-Key: pq_test_demo_key_REPLACE_ME'
```

### Aggregate liability exposure

`GET /api/v1/analytics/liability`

```bash curl theme={null}
curl -s https://api.precipiq.dev/api/v1/analytics/liability \
  -H 'X-Precipiq-Key: pq_test_demo_key_REPLACE_ME'
```

### Create and download a forensic export

`POST /api/v1/exports` → `GET /api/v1/exports/{id}` → `GET /api/v1/exports/{id}/download`

Forensic exports are generated asynchronously. Start the job, poll until it completes, then download the signed bundle.

```bash curl theme={null}
# 1. Start the export.
EXPORT_ID=$(curl -s -X POST https://api.precipiq.dev/api/v1/exports \
  -H 'X-Precipiq-Key: pq_test_demo_key_REPLACE_ME' \
  -H 'Content-Type: application/json' \
  -d '{"export_type":"forensic","start_date":"2026-01-01T00:00:00Z","end_date":"2026-03-31T23:59:59Z"}' \
  | jq -r .data.export_id)

# 2. Poll until complete.
while true; do
  STATUS=$(curl -s "https://api.precipiq.dev/api/v1/exports/$EXPORT_ID" \
    -H 'X-Precipiq-Key: pq_test_demo_key_REPLACE_ME' | jq -r .data.status)
  [ "$STATUS" = "complete" ] && break
  sleep 5
done

# 3. Download the signed bundle.
curl -s "https://api.precipiq.dev/api/v1/exports/$EXPORT_ID/download" \
  -H 'X-Precipiq-Key: pq_test_demo_key_REPLACE_ME' \
  -o precipiq-export.zip
```

<Note>The download URL in the response is a pre-signed link with a limited TTL. Download the file promptly after the export reaches `complete` status.</Note>
