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

# Financial Events: tracking dollar outcomes

> A financial event records a real dollar movement — payment, refund, chargeback, or cost — independently of any AI decision that may have caused it.

A financial event is a real dollar that moved. It is deliberately separate from decision records: a payment, a refund, a chargeback, an internal cost booking, or an estimated liability exists regardless of whether any AI decision caused it. The ledger's value comes from the links between decisions and events, not from conflating them. Once written, events are immutable — and the `amount` field is always signed (negative for refunds and chargebacks), so P\&L aggregates are a simple SUM with no per-type sign logic scattered across queries.

## How events reach the ledger

Events arrive from three sources:

<Steps>
  <Step title="Stripe and QuickBooks webhooks">
    The Precipiq API has built-in receivers for both. Point your webhook at the Precipiq endpoint, set the `X-Precipiq-Key` header on the endpoint configuration, and events land automatically — no code required.
  </Step>

  <Step title="SDK-ingested events">
    For internal systems such as your own billing engine or ops event stream, POST events directly to the REST API. The SDK does not yet wrap this endpoint, so the REST call is the recommended path.
  </Step>

  <Step title="Manual entry">
    The dashboard supports manual event creation for backfill and one-off adjustments. This is especially useful during onboarding when you need to seed historical data.
  </Step>
</Steps>

## Shape

```json theme={null}
{
  "id": "fev_abc",
  "org_id": "o_...",
  "source": "stripe",
  "event_type": "payment",
  "amount": "99.99",
  "currency": "USD",
  "external_id": "ch_3P7xYz...",
  "timestamp": "2026-04-16T12:35:02.000000Z",
  "created_at": "2026-04-16T12:35:03.120000Z"
}
```

## Ingesting from code

<CodeGroup>
  ```python Python theme={null}
  import httpx

  r = httpx.post(
      "https://api.precipiq.dev/api/v1/financial-events",
      headers={"X-Precipiq-Key": "pq_test_demo_key_REPLACE_ME"},
      json={
          "source": "internal",
          "event_type": "payment",
          "amount": "149.00",          # signed Decimal string
          "currency": "USD",
          "external_id": "invoice_0042",   # idempotency key
          "timestamp": "2026-04-16T12:35:02Z",
      },
      timeout=10.0,
  )
  r.raise_for_status()
  event = r.json()["data"]
  print(event["event_id"])
  ```

  ```typescript TypeScript theme={null}
  const res = await fetch(
    'https://api.precipiq.dev/api/v1/financial-events',
    {
      method: 'POST',
      headers: {
        'X-Precipiq-Key': 'pq_test_demo_key_REPLACE_ME',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        source: 'internal',
        event_type: 'payment',
        amount: '149.00',
        currency: 'USD',
        external_id: 'invoice_0042',
        timestamp: '2026-04-16T12:35:02Z',
      }),
    },
  );
  if (!res.ok) throw new Error(`ingest failed: ${res.status}`);
  const event = (await res.json()).data;
  console.log(event.event_id);
  ```
</CodeGroup>

<Info>
  Dedicated SDK methods (`pq.log_financial_event` / `pq.logFinancialEvent`) are planned but not in the current SDK release. Today the REST call above is the recommended approach for code paths outside Stripe and QuickBooks.
</Info>

## Event types

| Type         | Sign | Typical source                    |
| ------------ | ---- | --------------------------------- |
| `payment`    | +    | Stripe `payment_intent.succeeded` |
| `refund`     | −    | Stripe `charge.refunded`          |
| `chargeback` | −    | Stripe `charge.dispute.created`   |
| `cost`       | −    | Internal COGS / API spend         |
| `liability`  | −    | Booked legal/compliance exposure  |
| `other`      | ±    | Caller-supplied                   |

## Idempotency

Every event accepts an optional `external_id` field. When a Stripe webhook retries the same event (Stripe does this aggressively), the Precipiq receiver returns the existing row and writes no duplicate. Your integration code does not need to track delivery state.

<Tip>
  Always supply `external_id` for events sourced from external systems. For Stripe, use the Stripe event ID. For internal systems, use your invoice or transaction ID.
</Tip>

## Why events and decisions are separate

Separating decisions from events is what makes Precipiq useful as an evidentiary record. A claim like "the AI caused this refund" decomposes into two independently verifiable statements: the AI made decision X, and decision X is linked to refund Y with a measured correlation. Each side stands alone, and the link itself is auditable. See [Consequence Links](/concepts/consequence-links) for how to create those links.
