Skip to main content
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:
1

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

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

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.

Shape

{
  "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

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"])
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.

Event types

TypeSignTypical source
payment+Stripe payment_intent.succeeded
refundStripe charge.refunded
chargebackStripe charge.dispute.created
costInternal COGS / API spend
liabilityBooked 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.
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.

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 for how to create those links.